/* packet-umts_fp.c
 * Routines for UMTS FP disassembly
 *
 * Martin Mathieson
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include <epan/packet.h>
#include <epan/expert.h>
#include <epan/prefs.h>
#include <epan/conversation.h>
#include <epan/proto_data.h>

#include <wsutil/crc7.h> /* For FP data header and control frame CRC. */
#include <wsutil/crc16-plain.h> /* For FP Payload CRC. */
#include <wsutil/crc11.h> /* For FP EDCH header CRC. */
#include <wsutil/pint.h>

#include "packet-umts_fp.h"
#include "packet-nbap.h"
#include "packet-rrc.h"

/* The Frame Protocol (FP) is described in:
 * 3GPP TS 25.427 (for dedicated channels)
 * 3GPP TS 25.435 (for common/shared channels)
 *
 * TODO:
 *  - IUR interface-specific formats
 *  - do CRC verification before further parsing
 *  - Set the logical channel properly for non multiplexed, channels
 *    for channels that doesn't have the C/T field! This should be based
 *    on the RRC message RadioBearerSetup.
 *  - E-DCH T2 heuristic dissector
 */
void proto_register_fp(void);
void proto_reg_handoff_fp(void);

/* Initialize the protocol and registered fields. */

int proto_fp = -1;
extern int proto_umts_mac;
extern int proto_umts_rlc;

static int hf_fp_release = -1;
static int hf_fp_release_version = -1;
static int hf_fp_release_year = -1;
static int hf_fp_release_month = -1;
static int hf_fp_channel_type = -1;
static int hf_fp_division = -1;
static int hf_fp_direction = -1;
static int hf_fp_ddi_config = -1;
static int hf_fp_ddi_config_ddi = -1;
static int hf_fp_ddi_config_macd_pdu_size = -1;

static int hf_fp_header_crc = -1;
static int hf_fp_ft = -1;
static int hf_fp_cfn = -1;
static int hf_fp_pch_cfn = -1;
static int hf_fp_pch_toa = -1;
static int hf_fp_cfn_control = -1;
static int hf_fp_toa = -1;
static int hf_fp_tfi = -1;
static int hf_fp_usch_tfi = -1;
static int hf_fp_cpch_tfi = -1;
static int hf_fp_propagation_delay = -1;
static int hf_fp_tb = -1;
static int hf_fp_chan_zero_tbs = -1;
static int hf_fp_received_sync_ul_timing_deviation = -1;
static int hf_fp_pch_pi = -1;
static int hf_fp_pch_tfi = -1;
static int hf_fp_fach_tfi = -1;
static int hf_fp_transmit_power_level = -1;
static int hf_fp_paging_indication_bitmap = -1;
static int hf_fp_relevant_paging_indication_bitmap = -1;
static int hf_fp_pdsch_set_id = -1;
static int hf_fp_rx_timing_deviation = -1;
static int hf_fp_dch_e_rucch_flag = -1;
static int hf_fp_dch_control_frame_type = -1;
static int hf_fp_dch_rx_timing_deviation = -1;
static int hf_fp_quality_estimate = -1;
static int hf_fp_payload_crc = -1;
static int hf_fp_edch_header_crc = -1;
static int hf_fp_edch_fsn = -1;
static int hf_fp_edch_subframe = -1;
static int hf_fp_edch_number_of_subframes = -1;
static int hf_fp_edch_harq_retransmissions = -1;
static int hf_fp_edch_subframe_number = -1;
static int hf_fp_edch_number_of_mac_es_pdus = -1;
static int hf_fp_edch_ddi = -1;
static int hf_fp_edch_subframe_header = -1;
static int hf_fp_edch_number_of_mac_d_pdus = -1;
static int hf_fp_edch_pdu_padding = -1;
static int hf_fp_edch_tsn = -1;
static int hf_fp_edch_mac_es_pdu = -1;

static int hf_fp_edch_user_buffer_size = -1;
static int hf_fp_edch_no_macid_sdus = -1;
static int hf_fp_edch_number_of_mac_is_pdus = -1;
static int hf_fp_edch_mac_is_pdu = -1;

static int hf_fp_edch_e_rnti = -1;
static int hf_fp_edch_macis_descriptors = -1;
static int hf_fp_edch_macis_lchid = -1;
static int hf_fp_edch_macis_length = -1;
static int hf_fp_edch_macis_flag = -1;
static int hf_fp_edch_entity = -1;

static int hf_fp_frame_seq_nr = -1;
static int hf_fp_hsdsch_pdu_block_header = -1;
/* static int hf_fp_hsdsch_pdu_block = -1; */
static int hf_fp_flush = -1;
static int hf_fp_fsn_drt_reset = -1;
static int hf_fp_drt_indicator = -1;
static int hf_fp_fach_indicator = -1;
static int hf_fp_total_pdu_blocks = -1;
static int hf_fp_drt = -1;
static int hf_fp_hrnti = -1;
static int hf_fp_rach_measurement_result = -1;
static int hf_fp_lchid = -1;
static int hf_fp_pdu_length_in_block = -1;
static int hf_fp_pdus_in_block = -1;
static int hf_fp_cmch_pi = -1;
static int hf_fp_user_buffer_size = -1;
static int hf_fp_hsdsch_credits = -1;
static int hf_fp_hsdsch_max_macd_pdu_len = -1;
static int hf_fp_hsdsch_max_macdc_pdu_len = -1;
static int hf_fp_hsdsch_interval = -1;
static int hf_fp_hsdsch_calculated_rate = -1;
static int hf_fp_hsdsch_unlimited_rate = -1;
static int hf_fp_hsdsch_repetition_period = -1;
static int hf_fp_hsdsch_data_padding = -1;
static int hf_fp_hsdsch_new_ie_flags = -1;
static int hf_fp_hsdsch_new_ie_flag[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
static int hf_fp_hsdsch_drt = -1;
static int hf_fp_hsdsch_entity = -1;
static int hf_fp_hsdsch_physical_layer_category = -1;
static int hf_fp_timing_advance = -1;
static int hf_fp_num_of_pdu = -1;
static int hf_fp_mac_d_pdu_len = -1;
static int hf_fp_mac_d_pdu = -1;
static int hf_fp_data = -1;
static int hf_fp_crcis = -1;
static int hf_fp_crci[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
static int hf_fp_common_control_frame_type = -1;
static int hf_fp_t1 = -1;
static int hf_fp_t2 = -1;
static int hf_fp_t3 = -1;
static int hf_fp_ul_sir_target = -1;
static int hf_fp_pusch_set_id = -1;
static int hf_fp_activation_cfn = -1;
static int hf_fp_duration = -1;
static int hf_fp_power_offset = -1;
static int hf_fp_code_number = -1;
static int hf_fp_spreading_factor = -1;
static int hf_fp_mc_info = -1;

static int hf_fp_rach_new_ie_flags = -1;
static int hf_fp_rach_new_ie_flag_unused[7] = {-1, -1, -1, -1, -1, -1, -1 };
static int hf_fp_rach_ext_propagation_delay_present = -1;
static int hf_fp_rach_cell_portion_id_present = -1;
static int hf_fp_rach_angle_of_arrival_present = -1;
static int hf_fp_rach_ext_rx_sync_ul_timing_deviation_present = -1;
static int hf_fp_rach_ext_rx_timing_deviation_present = -1;

static int hf_fp_cell_portion_id = -1;
static int hf_fp_ext_propagation_delay = -1;
static int hf_fp_angle_of_arrival = -1;
static int hf_fp_ext_received_sync_ul_timing_deviation = -1;

static int hf_fp_radio_interface_parameter_update_flag[5] = {-1, -1, -1, -1, -1};
static int hf_fp_dpc_mode = -1;
static int hf_fp_tpc_po = -1;
static int hf_fp_multiple_rl_set_indicator = -1;
static int hf_fp_max_ue_tx_pow = -1;
static int hf_fp_congestion_status = -1;
static int hf_fp_e_rucch_present = -1;
static int hf_fp_extended_bits_present = -1;
static int hf_fp_extended_bits = -1;
static int hf_fp_spare_extension = -1;
static int hf_fp_ul_setup_frame = -1;
static int hf_fp_dl_setup_frame = -1;
static int hf_fp_relevant_pi_frame = -1;

/* Subtrees. */
static int ett_fp = -1;
static int ett_fp_release = -1;
static int ett_fp_data = -1;
static int ett_fp_crcis = -1;
static int ett_fp_ddi_config = -1;
static int ett_fp_edch_subframe_header = -1;
static int ett_fp_edch_subframe = -1;
static int ett_fp_edch_maces = -1;
static int ett_fp_edch_macis_descriptors = -1;
static int ett_fp_hsdsch_new_ie_flags = -1;
static int ett_fp_rach_new_ie_flags = -1;
static int ett_fp_hsdsch_pdu_block_header = -1;
static int ett_fp_pch_relevant_pi = -1;

static expert_field ei_fp_hsdsch_common_experimental_support = EI_INIT;
static expert_field ei_fp_hsdsch_common_t3_not_implemented = EI_INIT;
static expert_field ei_fp_channel_type_unknown = EI_INIT;
static expert_field ei_fp_ddi_not_defined = EI_INIT;
static expert_field ei_fp_stop_hsdpa_transmission = EI_INIT;
static expert_field ei_fp_hsdsch_entity_not_specified = EI_INIT;
static expert_field ei_fp_expecting_tdd = EI_INIT;
static expert_field ei_fp_bad_payload_checksum = EI_INIT;
static expert_field ei_fp_e_rnti_t2_edch_frames = EI_INIT;
static expert_field ei_fp_crci_no_subdissector = EI_INIT;
static expert_field ei_fp_timing_adjustmentment_reported = EI_INIT;
static expert_field ei_fp_mac_is_sdus_miscount = EI_INIT;
static expert_field ei_fp_maybe_srb = EI_INIT;
static expert_field ei_fp_transport_channel_type_unknown = EI_INIT;
static expert_field ei_fp_pch_lost_relevant_pi_frame = EI_INIT;
static expert_field ei_fp_unable_to_locate_ddi_entry = EI_INIT;
static expert_field ei_fp_e_rnti_first_entry = EI_INIT;
static expert_field ei_fp_bad_header_checksum = EI_INIT;
static expert_field ei_fp_crci_error_bit_set_for_tb = EI_INIT;
static expert_field ei_fp_spare_extension = EI_INIT;
static expert_field ei_fp_no_per_frame_info = EI_INIT;
static expert_field ei_fp_no_per_conv_channel_info = EI_INIT;
static expert_field ei_fp_invalid_frame_count = EI_INIT;

static dissector_handle_t rlc_bcch_handle;
static dissector_handle_t mac_fdd_dch_handle;
static dissector_handle_t mac_fdd_rach_handle;
static dissector_handle_t mac_fdd_fach_handle;
static dissector_handle_t mac_fdd_pch_handle;
static dissector_handle_t mac_fdd_edch_handle;
static dissector_handle_t mac_fdd_edch_type2_handle;
static dissector_handle_t mac_fdd_hsdsch_handle;
static dissector_handle_t fp_handle;

static proto_tree *top_level_tree = NULL;

/* Variables used for preferences */
static gboolean preferences_call_mac_dissectors = TRUE;
static gboolean preferences_show_release_info = TRUE;
static gboolean preferences_payload_checksum = TRUE;
static gboolean preferences_header_checksum = TRUE;
static gboolean preferences_track_paging_indications = TRUE;

/* E-DCH (T1) channel header information */
struct edch_t1_subframe_info
{
    guint8  subframe_number;
    guint8  number_of_mac_es_pdus;
    guint8  ddi[64];
    guint16 number_of_mac_d_pdus[64];
};

/* E-DCH (T2) channel header information */
struct edch_t2_subframe_info
{
    guint8  subframe_number;
    guint8  number_of_mac_is_pdus;
    guint8  number_of_mac_is_sdus[16];
    guint8  mac_is_lchid[16][16];
    guint16 mac_is_length[16][16];
};


static const value_string channel_type_vals[] =
{
    { CHANNEL_RACH_FDD,         "RACH_FDD" },
    { CHANNEL_RACH_TDD,         "RACH_TDD" },
    { CHANNEL_FACH_FDD,         "FACH_FDD" },
    { CHANNEL_FACH_TDD,         "FACH_TDD" },
    { CHANNEL_DSCH_FDD,         "DSCH_FDD" },
    { CHANNEL_DSCH_TDD,         "DSCH_TDD" },
    { CHANNEL_USCH_TDD_384,     "USCH_TDD_384" },
    { CHANNEL_USCH_TDD_128,     "USCH_TDD_128" },
    { CHANNEL_PCH,              "PCH" },
    { CHANNEL_CPCH,             "CPCH" },
    { CHANNEL_BCH,              "BCH" },
    { CHANNEL_DCH,              "DCH" },
    { CHANNEL_HSDSCH,           "HSDSCH" },
    { CHANNEL_IUR_CPCHF,        "IUR CPCHF" },
    { CHANNEL_IUR_FACH,         "IUR FACH" },
    { CHANNEL_IUR_DSCH,         "IUR DSCH" },
    { CHANNEL_EDCH,             "EDCH" },
    { CHANNEL_RACH_TDD_128,     "RACH_TDD_128" },
    { CHANNEL_HSDSCH_COMMON,    "HSDSCH-COMMON" },
    { CHANNEL_HSDSCH_COMMON_T3, "HSDSCH-COMMON-T3" },
    { CHANNEL_EDCH_COMMON,      "EDCH-COMMON"},
    { 0, NULL }
};

static const value_string division_vals[] =
{
    { Division_FDD,      "FDD"},
    { Division_TDD_384,  "TDD-384"},
    { Division_TDD_128,  "TDD-128"},
    { Division_TDD_768,  "TDD-768"},
    { 0, NULL }
};


static const value_string data_control_vals[] = {
    { 0,   "Data" },
    { 1,   "Control" },
    { 0,   NULL }
};

static const value_string direction_vals[] = {
    { 0,   "Downlink" },
    { 1,   "Uplink" },
    { 0,   NULL }
};

static const value_string crci_vals[] = {
    { 0,   "Correct" },
    { 1,   "Not correct" },
    { 0,   NULL }
};

static const value_string paging_indication_vals[] = {
    { 0,   "no PI-bitmap in payload" },
    { 1,   "PI-bitmap in payload" },
    { 0,   NULL }
};

static const value_string spreading_factor_vals[] = {
    { 0,   "4"},
    { 1,   "8"},
    { 2,   "16"},
    { 3,   "32"},
    { 4,   "64"},
    { 5,   "128"},
    { 6,   "256"},
    { 0,   NULL }
};

static const value_string congestion_status_vals[] = {
    { 0,   "No TNL congestion"},
    { 1,   "Reserved for future use"},
    { 2,   "TNL congestion - detected by delay build-up"},
    { 3,   "TNL congestion - detected by frame loss"},
    { 0,   NULL }
};

static const value_string e_rucch_flag_vals[] = {
    { 0,   "Conventional E-RUCCH reception" },
    { 1,   "TA Request reception" },
    { 0,   NULL }
};

static const value_string hsdshc_mac_entity_vals[] = {
    { entity_not_specified,    "Unspecified (assume MAC-hs)" },
    { hs,                      "MAC-hs" },
    { ehs,                     "MAC-ehs" },
    { 0,   NULL }
};

static const value_string edch_mac_entity_vals[] = {
    { 0,                    "MAC-e/es" },
    { 1,                    "MAC-i/is" },
    { 0,   NULL }
};

static const value_string lchid_vals[] = {
    {  0,   "Logical Channel 1" },
    {  1,   "Logical Channel 2" },
    {  2,   "Logical Channel 3" },
    {  3,   "Logical Channel 4" },
    {  4,   "Logical Channel 5" },
    {  5,   "Logical Channel 6" },
    {  6,   "Logical Channel 7" },
    {  7,   "Logical Channel 8" },
    {  8,   "Logical Channel 9" },
    {  9,   "Logical Channel 10" },
    { 10,   "Logical Channel 11" },
    { 11,   "Logical Channel 12" },
    { 12,   "Logical Channel 13" },
    { 13,   "Logical Channel 14" },
    { 14,   "CCCH (SRB0)" },
    { 15,   "E-RNTI being included (FDD only)" },
    { 0,   NULL }
};

/* Dedicated control types */
#define DCH_OUTER_LOOP_POWER_CONTROL            1
#define DCH_TIMING_ADJUSTMENT                   2
#define DCH_DL_SYNCHRONISATION                  3
#define DCH_UL_SYNCHRONISATION                  4

#define DCH_DL_NODE_SYNCHRONISATION             6
#define DCH_UL_NODE_SYNCHRONISATION             7
#define DCH_RX_TIMING_DEVIATION                 8
#define DCH_RADIO_INTERFACE_PARAMETER_UPDATE    9
#define DCH_TIMING_ADVANCE                     10
#define DCH_TNL_CONGESTION_INDICATION          11

static const value_string dch_control_frame_type_vals[] = {
    { DCH_OUTER_LOOP_POWER_CONTROL,         "OUTER LOOP POWER CONTROL" },
    { DCH_TIMING_ADJUSTMENT,                "TIMING ADJUSTMENT" },
    { DCH_DL_SYNCHRONISATION,               "DL SYNCHRONISATION" },
    { DCH_UL_SYNCHRONISATION,               "UL SYNCHRONISATION" },
    { 5,                                    "Reserved Value" },
    { DCH_DL_NODE_SYNCHRONISATION,          "DL NODE SYNCHRONISATION" },
    { DCH_UL_NODE_SYNCHRONISATION,          "UL NODE SYNCHRONISATION" },
    { DCH_RX_TIMING_DEVIATION,              "RX TIMING DEVIATION" },
    { DCH_RADIO_INTERFACE_PARAMETER_UPDATE, "RADIO INTERFACE PARAMETER UPDATE" },
    { DCH_TIMING_ADVANCE,                   "TIMING ADVANCE" },
    { DCH_TNL_CONGESTION_INDICATION,        "TNL CONGESTION INDICATION" },
    { 0,   NULL }
};


/* Common channel control types */
#define COMMON_OUTER_LOOP_POWER_CONTROL                1
#define COMMON_TIMING_ADJUSTMENT                       2
#define COMMON_DL_SYNCHRONISATION                      3
#define COMMON_UL_SYNCHRONISATION                      4

#define COMMON_DL_NODE_SYNCHRONISATION                 6
#define COMMON_UL_NODE_SYNCHRONISATION                 7
#define COMMON_DYNAMIC_PUSCH_ASSIGNMENT                8
#define COMMON_TIMING_ADVANCE                          9
#define COMMON_HS_DSCH_Capacity_Request               10
#define COMMON_HS_DSCH_Capacity_Allocation            11
#define COMMON_HS_DSCH_Capacity_Allocation_Type_2     12

static const value_string common_control_frame_type_vals[] = {
    { COMMON_OUTER_LOOP_POWER_CONTROL,            "OUTER LOOP POWER CONTROL" },
    { COMMON_TIMING_ADJUSTMENT,                   "TIMING ADJUSTMENT" },
    { COMMON_DL_SYNCHRONISATION,                  "DL SYNCHRONISATION" },
    { COMMON_UL_SYNCHRONISATION,                  "UL SYNCHRONISATION" },
    { 5,                                          "Reserved Value" },
    { COMMON_DL_NODE_SYNCHRONISATION,             "DL NODE SYNCHRONISATION" },
    { COMMON_UL_NODE_SYNCHRONISATION,             "UL NODE SYNCHRONISATION" },
    { COMMON_DYNAMIC_PUSCH_ASSIGNMENT,            "DYNAMIC PUSCH ASSIGNMENT" },
    { COMMON_TIMING_ADVANCE,                      "TIMING ADVANCE" },
    { COMMON_HS_DSCH_Capacity_Request,            "HS-DSCH Capacity Request" },
    { COMMON_HS_DSCH_Capacity_Allocation,         "HS-DSCH Capacity Allocation" },
    { COMMON_HS_DSCH_Capacity_Allocation_Type_2,  "HS-DSCH Capacity Allocation Type 2" },
    { 0,   NULL }
};

/* 0 to 7*/
static const guint8 hsdsch_macdflow_id_rlc_map[] = {
    RLC_UM,                   /*0 SRB */
    RLC_AM,                   /*1 Interactive PS*/
    RLC_AM,                   /*2 Interatcive PS*/
    RLC_UNKNOWN_MODE,         /*3 ???*/
    RLC_AM,                   /*4 Streaming PS*/
    RLC_UNKNOWN_MODE,
    RLC_UNKNOWN_MODE,
    RLC_UNKNOWN_MODE
};

/* Mapping hsdsch MACd-FlowId to MAC_CONTENT, basically flowid = 1 (0) => SRB*/
/* 1 to 8*/
static const guint8 hsdsch_macdflow_id_mac_content_map[] = {
    MAC_CONTENT_DCCH,    /*1 SRB */
    MAC_CONTENT_PS_DTCH, /*2 Interactive PS*/
    MAC_CONTENT_PS_DTCH, /*3 Interatcive PS*/
    RLC_UNKNOWN_MODE,    /*4 ???*/
    MAC_CONTENT_PS_DTCH, /*5 Streaming PS*/
    RLC_UNKNOWN_MODE,
    RLC_UNKNOWN_MODE,
    RLC_UNKNOWN_MODE
};

/* Make fake logical channel id's based on MACdFlow-ID's,
* XXXX Bug 12121 expanded the number of entries to 8(+2),
* not at all sure what the proper value should be 0xfF?
*/
static const guint8 fake_lchid_macd_flow[] = {1,9,14,11,0,12,0,0};

/* Dissect message parts */
static int dissect_tb_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                           int offset, struct fp_info *p_fp_info,
                           dissector_handle_t *data_handle,
                           void *data);

static int dissect_macd_pdu_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                 int offset, guint16 length, guint16 number_of_pdus, struct fp_info *p_fp_info,
                                 void *data);
static int dissect_macd_pdu_data_type_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                        int offset, guint16 length, guint16 number_of_pdus, struct fp_info * fpi,
                                        void *data);

static int dissect_crci_bits(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                             fp_info *p_fp_info, int offset);
static void dissect_spare_extension_and_crc(tvbuff_t *tvb, packet_info *pinfo,
                                            proto_tree *tree, guint8 dch_crc_present,
                                            int offset, guint header_length);
/* Dissect common control messages */
static int dissect_common_outer_loop_power_control(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb,
                                                   int offset, struct fp_info *p_fp_info);
static int dissect_common_timing_adjustment(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb,
                                            int offset, struct fp_info *p_fp_info);
static int dissect_common_dl_node_synchronisation(packet_info *pinfo, proto_tree *tree,
                                                  tvbuff_t *tvb, int offset);
static int dissect_common_ul_node_synchronisation(packet_info *pinfo, proto_tree *tree,
                                                  tvbuff_t *tvb, int offset);
static int dissect_common_dl_synchronisation(packet_info *pinfo, proto_tree *tree,
                                            tvbuff_t *tvb, int offset,
                                            struct fp_info *p_fp_info);
static int dissect_common_ul_synchronisation(packet_info *pinfo, proto_tree *tree,
                                            tvbuff_t *tvb, int offset,
                                            struct fp_info *p_fp_info);
static int dissect_common_timing_advance(packet_info *pinfo, proto_tree *tree,
                                         tvbuff_t *tvb, int offset);
static int dissect_hsdpa_capacity_request(packet_info *pinfo, proto_tree *tree,
                                          tvbuff_t *tvb, int offset);
static int dissect_hsdpa_capacity_allocation(packet_info *pinfo, proto_tree *tree,
                                             tvbuff_t *tvb, int offset,
                                             struct fp_info *p_fp_info);
static int dissect_hsdpa_capacity_allocation_type_2(packet_info *pinfo, proto_tree *tree,
                                                    tvbuff_t *tvb, int offset);
static void dissect_common_control(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                   int offset, struct fp_info *p_fp_info);
static int dissect_common_dynamic_pusch_assignment(packet_info *pinfo, proto_tree *tree,
                                                   tvbuff_t *tvb, int offset);

/* Dissect common channel types */
static void dissect_rach_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                      int offset, struct fp_info *p_fp_info,
                                      void *data);
static void dissect_fach_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                      int offset, struct fp_info *p_fp_info,
                                      void *data);
static void dissect_dsch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                      int offset, struct fp_info *p_fp_info);
static void dissect_usch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                      int offset, struct fp_info *p_fp_info);
static void dissect_pch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                     int offset, struct fp_info *p_fp_info,
                                     void *data);
static void dissect_cpch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                      int offset, struct fp_info *p_fp_info);
static void dissect_bch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                     int offset, struct fp_info *p_fp_info);
static void dissect_iur_dsch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                          int offset, struct fp_info *p_fp_info);
static void dissect_hsdsch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                        int offset, struct fp_info *p_fp_info,
                                        void *data);
static void dissect_hsdsch_type_2_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                               int offset, struct fp_info *p_fp_info,
                                               void *data);
static void dissect_hsdsch_common_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                               int offset,
                                               struct fp_info *p_fp_info,
                                               void *data);

/* Dissect DCH control messages */
static int dissect_dch_timing_adjustment(proto_tree *tree, packet_info *pinfo,
                                         tvbuff_t *tvb, int offset);
static int dissect_dch_rx_timing_deviation(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset,
                                           struct fp_info *p_fp_info);
static int dissect_dch_dl_synchronisation(proto_tree *tree, packet_info *pinfo,
                                          tvbuff_t *tvb, int offset);
static int dissect_dch_ul_synchronisation(proto_tree *tree, packet_info *pinfo,
                                          tvbuff_t *tvb, int offset);
static int dissect_dch_outer_loop_power_control(proto_tree *tree, packet_info *pinfo,
                                                tvbuff_t *tvb, int offset);
static int dissect_dch_dl_node_synchronisation(proto_tree *tree, packet_info *pinfo,
                                               tvbuff_t *tvb, int offset);
static int dissect_dch_ul_node_synchronisation(proto_tree *tree, packet_info *pinfo,
                                               tvbuff_t *tvb, int offset);
static int dissect_dch_radio_interface_parameter_update(proto_tree *tree, packet_info *pinfo,
                                                        tvbuff_t *tvb, int offset);
static int dissect_dch_timing_advance(proto_tree *tree, packet_info *pinfo,
                                      tvbuff_t *tvb, int offset, struct fp_info *p_fp_info);
static int dissect_dch_tnl_congestion_indication(proto_tree *tree, packet_info *pinfo,
                                                 tvbuff_t *tvb, int offset);


static void dissect_dch_control_frame(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb,
                                      int offset, struct fp_info *p_fp_info);


/* Dissect a DCH channel */
static void dissect_dch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                     int offset, struct fp_info *p_fp_info,
                                     void *data);

/* Dissect dedicated channels */
static void dissect_e_dch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                       int offset, struct fp_info *p_fp_info,
                                       gboolean is_common,
                                       void *data);

static void dissect_e_dch_t2_or_common_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                                    int offset, struct fp_info *p_fp_info,
                                                    int number_of_subframes,
                                                    gboolean is_common,
                                                    guint16 header_crc,
                                                    proto_item * header_crc_pi,
                                                    void *data);

/* Main dissection function */
static int dissect_fp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data);

/*
 * CRNC sends data downlink on uplink parameters.
 */
void
set_umts_fp_conv_data(conversation_t *conversation, umts_fp_conversation_info_t *umts_fp_conversation_info)
{

    if (conversation == NULL) {
        return;
     }

    conversation_add_proto_data(conversation, proto_fp, umts_fp_conversation_info);
}


static int
get_tb_count(struct fp_info *p_fp_info)
{
    int chan, tb_count = 0;
    for (chan = 0; chan < p_fp_info->num_chans; chan++) {
        tb_count += p_fp_info->chan_num_tbs[chan];
    }
    return tb_count;
}

static gboolean verify_control_frame_crc(tvbuff_t * tvb, packet_info * pinfo, proto_item * pi, guint16 frame_crc)
{
    guint8 crc = 0;
    guint8 * data = NULL;
    /* Get data. */
    data = (guint8 *)tvb_memdup(wmem_packet_scope(), tvb, 0, tvb_reported_length(tvb));
    /* Include only FT flag bit in CRC calculation. */
    data[0] = data[0] & 1;
    /* Calculate crc7 sum. */
    crc = crc7update(0, data, tvb_reported_length(tvb));
    crc = crc7finalize(crc); /* finalize crc */
    if (frame_crc == crc) {
        proto_item_append_text(pi, " [correct]");
        return TRUE;
    } else {
        proto_item_append_text(pi, " [incorrect, should be 0x%x]", crc);
        expert_add_info(pinfo, pi, &ei_fp_bad_header_checksum);
        return FALSE;
    }
}
static gboolean verify_header_crc(tvbuff_t * tvb, packet_info * pinfo, proto_item * pi, guint16 header_crc, guint header_length)
{
    guint8 crc = 0;
    guint8 * data = NULL;
    /* Get data of header with first byte removed. */
    data = (guint8 *)tvb_memdup(wmem_packet_scope(), tvb, 1, header_length-1);
    /* Calculate crc7 sum. */
    crc = crc7update(0, data, header_length-1);
    crc = crc7finalize(crc); /* finalize crc */
    if (header_crc == crc) {
        proto_item_append_text(pi, " [correct]");
        return TRUE;
    } else {
        proto_item_append_text(pi, " [incorrect, should be 0x%x]", crc);
        expert_add_info(pinfo, pi, &ei_fp_bad_header_checksum);
        return FALSE;
    }
}
static gboolean verify_header_crc_edch(tvbuff_t * tvb, packet_info * pinfo, proto_item * pi, guint16 header_crc, guint header_length)
{
    guint16 crc = 0;
    guint8 * data = NULL;
    /* First create new subset of header with first byte removed. */
    tvbuff_t * headtvb = tvb_new_subset_length(tvb, 1, header_length-1);
    /* Get data of header with first byte removed. */
    data = (guint8 *)tvb_memdup(wmem_packet_scope(), headtvb, 0, header_length-1);
    /* Remove first 4 bits of the remaining data which are Header CRC cont. */
    data[0] = data[0] & 0x0f;
    crc = crc11_307_noreflect_noxor(data, header_length-1);
    if (header_crc == crc) {
        proto_item_append_text(pi, " [correct]");
        return TRUE;
    } else {
        proto_item_append_text(pi, " [incorrect, should be 0x%x]", crc);
        expert_add_info(pinfo, pi, &ei_fp_bad_header_checksum);
        return FALSE;
    }
}

/* Dissect the TBs of a UL data frame*/
static int
dissect_tb_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                int offset, struct fp_info *p_fp_info,
                dissector_handle_t *data_handle, void *data)
{
    int         chan, num_tbs   = 0;
    int         bit_offset      = 0;
    int         crci_bit_offset = (offset+1)<<3; /* Current offset + Quality estimate of 1 byte at the end*/
    guint       data_bits       = 0;
    guint8      crci_bit        = 0;
    proto_item *tree_ti         = NULL;
    proto_tree *data_tree       = NULL;
    gboolean    dissected       = FALSE;

    /* Add data subtree */
    tree_ti =  proto_tree_add_item(tree, hf_fp_data, tvb, offset, -1, ENC_NA);
    proto_item_set_text(tree_ti, "TB data for %u chans", p_fp_info->num_chans);
    data_tree = proto_item_add_subtree(tree_ti, ett_fp_data);

    if (p_fp_info->num_chans >= MAX_MAC_FRAMES) {
        expert_add_info_format(pinfo, data_tree, &ei_fp_invalid_frame_count, "Invalid Number of channels (max is %u)", MAX_MAC_FRAMES);
        return offset;
    }

    /* Calculate offset to CRCI bits */

    if (p_fp_info->is_uplink) {
        for (chan=0; chan < p_fp_info->num_chans; chan++) {
            int n;
            for (n=0; n < p_fp_info->chan_num_tbs[chan]; n++) {
                /* Advance bit offset */
                crci_bit_offset += p_fp_info->chan_tf_size[chan];
                /* Pad out to next byte */
                if (crci_bit_offset % 8) {
                    crci_bit_offset += (8 - (crci_bit_offset % 8));
                }
            }
        }
    }
    /* Now for the TB data */
    for (chan=0; chan < p_fp_info->num_chans; chan++) {
        int n;
        p_fp_info->cur_chan = chan;    /*Set current channel?*/
        /* Clearly show channels with no TBs */
        if (p_fp_info->chan_num_tbs[chan] == 0) {
            proto_item *no_tb_ti = proto_tree_add_uint(data_tree, hf_fp_chan_zero_tbs, tvb,
                                                       offset+(bit_offset/8),
                                                       0, chan+1);
            proto_item_append_text(no_tb_ti, " (of size %d)",
                                   p_fp_info->chan_tf_size[chan]);
            proto_item_set_generated(no_tb_ti);
        }

        /* Show TBs from non-empty channels */
        pinfo->fd->subnum = chan; /* set subframe number to current TB */

        for (n=0; n < p_fp_info->chan_num_tbs[chan]; n++) {

            proto_item *ti;
            p_fp_info->cur_tb = chan;    /*Set current transport block?*/
            if (data_tree) {
                ti = proto_tree_add_item(data_tree, hf_fp_tb, tvb,
                                         offset + (bit_offset/8),
                                         ((bit_offset % 8) + p_fp_info->chan_tf_size[chan] + 7) / 8,
                                         ENC_NA);
                proto_item_set_text(ti, "TB (chan %u, tb %u, %u bits)",
                                    chan+1, n+1, p_fp_info->chan_tf_size[chan]);
            }

            if (preferences_call_mac_dissectors && data_handle &&
                (p_fp_info->chan_tf_size[chan] > 0)) {
                tvbuff_t *next_tvb;
                proto_item *item;
                /* If this is DL we should not care about crci bits (since they don't exists)*/
                if (p_fp_info->is_uplink) {


                    if ( p_fp_info->channel == CHANNEL_RACH_FDD) {    /*In RACH we don't have any QE field, hence go back 8 bits.*/
                        crci_bit = tvb_get_bits8(tvb, crci_bit_offset+n-8, 1);
                        item = proto_tree_add_item(data_tree, hf_fp_crci[n%8], tvb, (crci_bit_offset+n-8)/8, 1, ENC_BIG_ENDIAN);
                        proto_item_set_generated(item);
                    } else {
                        crci_bit = tvb_get_bits8(tvb, crci_bit_offset+n, 1);
                        item = proto_tree_add_item(data_tree, hf_fp_crci[n%8], tvb, (crci_bit_offset+n)/8, 1, ENC_BIG_ENDIAN);
                        proto_item_set_generated(item);
                    }
                }

                if (crci_bit == 0 || !p_fp_info->is_uplink) {
                    next_tvb = tvb_new_subset_length(tvb, offset + bit_offset/8,
                                              ((bit_offset % 8) + p_fp_info->chan_tf_size[chan] + 7) / 8);


                    /****************/
                    /* TODO: maybe this decision can be based only on info available in fp_info */
                    call_dissector_with_data(*data_handle, next_tvb, pinfo, top_level_tree, data);
                    dissected = TRUE;
                } else {
                    proto_tree_add_expert(tree, pinfo, &ei_fp_crci_no_subdissector, tvb, offset + bit_offset/8,
                                               ((bit_offset % 8) + p_fp_info->chan_tf_size[chan] + 7) / 8);
                }

            }
            num_tbs++;

            /* Advance bit offset */
            bit_offset += p_fp_info->chan_tf_size[chan];
            data_bits  += p_fp_info->chan_tf_size[chan];

            /* Pad out to next byte */
            if (bit_offset % 8) {
                bit_offset += (8 - (bit_offset % 8));
            }
        }
    }

    if (dissected == FALSE) {
        col_append_fstr(pinfo->cinfo, COL_INFO, "(%u bits in %u tbs)",
                        data_bits, num_tbs);
    }

    /* Data tree should cover entire length */
    if (data_tree) {
        proto_item_set_len(tree_ti, bit_offset/8);
        proto_item_append_text(tree_ti, " (%u bits in %u tbs)", data_bits, num_tbs);
    }

    /* Move offset past TBs (we know it's already padded out to next byte) */
    offset += (bit_offset / 8);

    return offset;
}


/* Dissect the MAC-d PDUs of an HS-DSCH (type 1) frame.
   Length is in bits, and payload is offset by 4 bits of padding */
static int
dissect_macd_pdu_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                      int offset, guint16 length, guint16 number_of_pdus,
                      struct fp_info *p_fp_info, void *data)
{
    int         pdu;
    int         bit_offset = 0;
    proto_item *pdus_ti    = NULL;
    proto_tree *data_tree  = NULL;
    gboolean    dissected  = FALSE;

    /* Add data subtree */
    pdus_ti =  proto_tree_add_item(tree, hf_fp_data, tvb, offset, -1, ENC_NA);
    proto_item_set_text(pdus_ti, "%u MAC-d PDUs of %u bits", number_of_pdus, length);
    data_tree = proto_item_add_subtree(pdus_ti, ett_fp_data);
    if (number_of_pdus >= MAX_MAC_FRAMES) {
        expert_add_info_format(pinfo, data_tree, &ei_fp_invalid_frame_count, "Invalid number_of_pdus (max is %u)", MAX_MAC_FRAMES);
        return offset;
    }

    /* Now for the PDUs */
    for (pdu=0; pdu < number_of_pdus; pdu++) {
        proto_item *pdu_ti;

        if (data_tree) {
            /* Show 4 bits padding at start of PDU */
            proto_tree_add_item(data_tree, hf_fp_hsdsch_data_padding, tvb, offset+(bit_offset/8), 1, ENC_BIG_ENDIAN);

        }
        bit_offset += 4;

        /* Data bytes! */
        if (data_tree) {
            pdu_ti = proto_tree_add_item(data_tree, hf_fp_mac_d_pdu, tvb,
                                         offset + (bit_offset/8),
                                         ((bit_offset % 8) + length + 7) / 8,
                                         ENC_NA);
            proto_item_set_text(pdu_ti, "MAC-d PDU (PDU %u)", pdu+1);
        }

        pinfo->fd->subnum = pdu; /* set subframe number to current TB */
        p_fp_info->cur_tb = pdu;    /*Set TB (PDU) index correctly*/
        if (preferences_call_mac_dissectors) {
            tvbuff_t *next_tvb;
            next_tvb = tvb_new_subset_length_caplen(tvb, offset + bit_offset/8,
                                      ((bit_offset % 8) + length + 7)/8, -1);
            call_dissector_with_data(mac_fdd_hsdsch_handle, next_tvb, pinfo, top_level_tree, data);
            dissected = TRUE;
        }

        /* Advance bit offset */
        bit_offset += length;

        /* Pad out to next byte */
        if (bit_offset % 8) {
            bit_offset += (8 - (bit_offset % 8));
        }
    }

    /* Data tree should cover entire length */
    proto_item_set_len(pdus_ti, bit_offset/8);

    /* Move offset past PDUs (we know it's already padded out to next byte) */
    offset += (bit_offset / 8);

    /* Show summary in info column */
    if (dissected == FALSE) {
        col_append_fstr(pinfo->cinfo, COL_INFO, "   %u PDUs of %u bits",
                        number_of_pdus, length);
    }

    return offset;
}


/* Dissect the MAC-d PDUs of an HS-DSCH (type 2) frame.
   Length is in bytes, and payload is byte-aligned (no padding) */
static int
dissect_macd_pdu_data_type_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                             int offset, guint16 length, guint16 number_of_pdus,
                             struct fp_info *fpi, void *data)
{
    int         pdu;
    proto_item *pdus_ti      = NULL;
    proto_tree *data_tree    = NULL;
    int         first_offset = offset;
    gboolean    dissected    = FALSE;

    /* Add data subtree */
    pdus_ti =  proto_tree_add_item(tree, hf_fp_data, tvb, offset, -1, ENC_NA);
    proto_item_set_text(pdus_ti, "%u MAC-d PDUs of %u bytes", number_of_pdus, length);
    data_tree = proto_item_add_subtree(pdus_ti, ett_fp_data);

    if (number_of_pdus >= MAX_MAC_FRAMES) {
        expert_add_info_format(pinfo, data_tree, &ei_fp_invalid_frame_count, "Invalid number_of_pdus (max is %u)", MAX_MAC_FRAMES);
        return offset;
    }

    /* Now for the PDUs */
    for (pdu=0; pdu < number_of_pdus; pdu++) {
        proto_item *pdu_ti;

        /* Data bytes! */
        if (data_tree) {
            pdu_ti = proto_tree_add_item(data_tree, hf_fp_mac_d_pdu, tvb,
                                         offset, length, ENC_NA);
            proto_item_set_text(pdu_ti, "MAC-d PDU (PDU %u)", pdu+1);

        }

        if (preferences_call_mac_dissectors) {

            tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, length);

            fpi->cur_tb = pdu;    /*Set proper pdu index for MAC and higher layers*/
            pinfo->fd->subnum = pdu;
            call_dissector_with_data(mac_fdd_hsdsch_handle, next_tvb, pinfo, top_level_tree, data);
            dissected = TRUE;
        }

        /* Advance offset */
        offset += length;
    }

    /* Data tree should cover entire length */
    proto_item_set_len(pdus_ti, offset-first_offset);

    /* Show summary in info column */
    if (!dissected) {
        col_append_fstr(pinfo->cinfo, COL_INFO, "   %u PDUs of %u bits",
                        number_of_pdus, length*8);
    }

    return offset;
}

/* Dissect CRCI bits (uplink) */
static int
dissect_crci_bits(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                  fp_info *p_fp_info, int offset)
{
    int         n, num_tbs;
    proto_item *ti         = NULL;
    proto_tree *crcis_tree = NULL;
    guint       errors     = 0;

    num_tbs = get_tb_count(p_fp_info);


    /* Add CRCIs subtree */
    if (tree) {
        ti =  proto_tree_add_item(tree, hf_fp_crcis, tvb, offset, (num_tbs+7)/8, ENC_NA);
        proto_item_set_text(ti, "CRCI bits for %u tbs", num_tbs);
        crcis_tree = proto_item_add_subtree(ti, ett_fp_crcis);
    }

    /* CRCIs */
    for (n=0; n < num_tbs; n++) {
        int bit = (tvb_get_guint8(tvb, offset+(n/8)) >> (7-(n%8))) & 0x01;
        proto_tree_add_item(crcis_tree, hf_fp_crci[n%8], tvb, offset+(n/8),
                            1, ENC_BIG_ENDIAN);

        if (bit == 1) {
            errors++;
            expert_add_info(pinfo, ti, &ei_fp_crci_error_bit_set_for_tb);
        }
    }

    if (tree) {
        /* Highlight range of bytes covered by indicator bits */
        proto_item_set_len(ti, (num_tbs+7) / 8);

        /* Show error count in root text */
        proto_item_append_text(ti, " (%u errors)", errors);
    }

    offset += ((num_tbs+7) / 8);
    return offset;
}


static void
dissect_spare_extension_and_crc(tvbuff_t *tvb, packet_info *pinfo,
                                proto_tree *tree, guint8 dch_crc_present,
                                int offset, guint header_length)
{
    int         crc_size = 0;
    int         remain   = tvb_captured_length_remaining(tvb, offset);

    /* Payload CRC (optional) */
    if ((dch_crc_present == 1) || ((dch_crc_present == 2) && (remain >= 2))) {
        crc_size = 2;
    }

    if (remain > crc_size) {
        proto_item *ti;
        ti = proto_tree_add_item(tree, hf_fp_spare_extension, tvb,
                                 offset, remain-crc_size, ENC_NA);
        proto_item_append_text(ti, " (%u octets)", remain-crc_size);
        expert_add_info_format(pinfo, ti, &ei_fp_spare_extension, "Spare Extension present (%u bytes)", remain-crc_size);
        offset += remain-crc_size;
    }

    if (crc_size) {
        proto_item * pi = proto_tree_add_item(tree, hf_fp_payload_crc, tvb, offset, crc_size,
                            ENC_BIG_ENDIAN);
        if (preferences_payload_checksum) {
            guint16 calc_crc, read_crc;
            if ((guint)offset > header_length) {
                guint8 * data = (guint8 *)tvb_memdup(wmem_packet_scope(), tvb, header_length, offset-header_length);
                calc_crc = crc16_8005_noreflect_noxor(data, offset-header_length);
            } else {
                calc_crc = 0;
            }
            read_crc = tvb_get_bits16(tvb, offset*8, 16, ENC_BIG_ENDIAN);

            if (calc_crc == read_crc) {
                proto_item_append_text(pi, " [correct]");
            } else {
                proto_item_append_text(pi, " [incorrect, should be 0x%x]", calc_crc);
                expert_add_info(pinfo, pi, &ei_fp_bad_payload_checksum);
            }
        }
    }
}

/***********************************************************/
/* Common control message types                            */

static int
dissect_common_outer_loop_power_control(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb,
                                        int offset, struct fp_info *p_fp_info _U_)
{
    return dissect_dch_outer_loop_power_control(tree, pinfo, tvb, offset);
}


static int
dissect_common_timing_adjustment(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb,
                                 int offset, struct fp_info *p_fp_info)
{
    gint32 toa;
    proto_item *toa_ti;

    if (p_fp_info->channel != CHANNEL_PCH) {
        guint32 cfn;

        /* CFN control */
        proto_tree_add_item_ret_uint(tree, hf_fp_cfn_control, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
        offset++;

        /* ToA */
        toa = tvb_get_ntohis(tvb, offset);
        toa_ti = proto_tree_add_item(tree, hf_fp_toa, tvb, offset, 2, ENC_BIG_ENDIAN);
        offset += 2;

        col_append_fstr(pinfo->cinfo, COL_INFO, "   CFN=%u, ToA=%d", cfn, toa);
    }
    else {
        guint32 cfn;

        /* PCH CFN is 12 bits */
        proto_tree_add_item_ret_uint(tree, hf_fp_pch_cfn, tvb, offset, 2, ENC_BIG_ENDIAN, &cfn);
        offset += 2;

        /* 4 bits of padding follow... */

        /* 20 bits of ToA (followed by 4 padding bits) */
        toa = ((int)(tvb_get_ntoh24(tvb, offset) << 8)) / 4096;
        toa_ti = proto_tree_add_int(tree, hf_fp_pch_toa, tvb, offset, 3, toa);
        offset += 3;

        col_append_fstr(pinfo->cinfo, COL_INFO, "   CFN=%u, ToA=%d", cfn, toa);
    }

    expert_add_info_format(pinfo, toa_ti, &ei_fp_timing_adjustmentment_reported, "Timing adjustmentment reported (%.3f ms)", ((float)(toa) / 8));

    return offset;
}

static int
dissect_common_dl_node_synchronisation(packet_info *pinfo, proto_tree *tree,
                                       tvbuff_t *tvb, int offset)
{
    /* T1 (3 bytes) */
    guint32 encoded = tvb_get_ntoh24(tvb, offset);
    float t1 = encoded * (float)0.125;
    proto_tree_add_float_format_value(tree, hf_fp_t1, tvb, offset, 3, t1, "%.3f ms (%u)", t1, encoded);
    offset += 3;

    col_append_fstr(pinfo->cinfo, COL_INFO, "   T1=%.3f", t1);

    return offset;
}

static int
dissect_common_ul_node_synchronisation(packet_info *pinfo, proto_tree *tree,
                                       tvbuff_t *tvb, int offset)
{
    guint32 encoded;
    float t1, t2, t3;

    /* T1 (3 bytes) */
    encoded = tvb_get_ntoh24(tvb, offset);
    t1 = encoded * (float)0.125;
    proto_tree_add_float_format_value(tree, hf_fp_t1, tvb, offset, 3, t1, "%.3f ms (%u)", t1, encoded);
    offset += 3;

    /* T2 (3 bytes) */
    encoded = tvb_get_ntoh24(tvb, offset);
    t2 = encoded * (float)0.125;
    proto_tree_add_float_format_value(tree, hf_fp_t2, tvb, offset, 3, t2, "%.3f ms (%u)", t2, encoded);
    offset += 3;

    /* T3 (3 bytes) */
    encoded = tvb_get_ntoh24(tvb, offset);
    t3 = encoded * (float)0.125;
    proto_tree_add_float_format_value(tree, hf_fp_t3, tvb, offset, 3, t3, "%.3f ms (%u)", t3, encoded);
    offset += 3;

    col_append_fstr(pinfo->cinfo, COL_INFO, "   T1=%.3f T2=%.3f, T3=%.3f",
                    t1, t2, t3);

    return offset;
}

static int
dissect_common_dl_synchronisation(packet_info *pinfo, proto_tree *tree,
                                  tvbuff_t *tvb, int offset, struct fp_info *p_fp_info)
{
    guint32 cfn;

    if (p_fp_info->channel != CHANNEL_PCH) {
        /* CFN control */
        proto_tree_add_item_ret_uint(tree, hf_fp_cfn_control, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
        offset++;
    }
    else {
        /* PCH CFN is 12 bits */
        proto_tree_add_item_ret_uint(tree, hf_fp_pch_cfn, tvb, offset, 2, ENC_BIG_ENDIAN, &cfn);

        /* 4 bits of padding follow... */
        offset += 2;
    }

    col_append_fstr(pinfo->cinfo, COL_INFO, "   CFN=%u", cfn);

    return offset;
}

static int
dissect_common_ul_synchronisation(packet_info *pinfo, proto_tree *tree,
                                  tvbuff_t *tvb, int offset, struct fp_info *p_fp_info)
{
    return dissect_common_timing_adjustment(pinfo, tree, tvb, offset, p_fp_info);
}

static int
dissect_common_timing_advance(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset)
{
    guint32 cfn;
    guint16 timing_advance;

    /* CFN control */
    proto_tree_add_item_ret_uint(tree, hf_fp_cfn_control, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
    offset++;

    /* Timing Advance */
    timing_advance = (tvb_get_guint8(tvb, offset) & 0x3f) * 4;
    proto_tree_add_uint(tree, hf_fp_timing_advance, tvb, offset, 1, timing_advance);
    offset++;

    col_append_fstr(pinfo->cinfo, COL_INFO, " CFN = %u, TA = %u",
                    cfn, timing_advance);

    return offset;
}

static int
dissect_hsdpa_capacity_request(packet_info *pinfo, proto_tree *tree,
                               tvbuff_t *tvb, int offset)
{
    guint8  priority;
    guint16 user_buffer_size;

    /* CmCH-PI */
    priority = (tvb_get_guint8(tvb, offset) & 0x0f);
    proto_tree_add_item(tree, hf_fp_cmch_pi, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;

    /* User buffer size */
    user_buffer_size = tvb_get_ntohs(tvb, offset);
    proto_tree_add_item(tree, hf_fp_user_buffer_size, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;

    col_append_fstr(pinfo->cinfo, COL_INFO, "      CmCH-PI=%u  User-Buffer-Size=%u",
                    priority, user_buffer_size);

    return offset;
}

static int
dissect_hsdpa_capacity_allocation(packet_info *pinfo, proto_tree *tree,
                                  tvbuff_t *tvb, int offset,
                                  struct fp_info *p_fp_info)
{
    proto_item *ti;
    proto_item *rate_ti;
    guint16     max_pdu_length;
    guint8      repetition_period;
    guint8      interval;
    guint64     credits;

    /* Congestion status (introduced sometime during R6...) */
    if ((p_fp_info->release == 6) || (p_fp_info->release == 7)) {
        proto_tree_add_bits_item(tree, hf_fp_congestion_status, tvb,
                                 offset*8 + 2, 2, ENC_BIG_ENDIAN);
    }

    /* CmCH-PI */
    proto_tree_add_item(tree, hf_fp_cmch_pi, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;

    /* Max MAC-d PDU length (13 bits) */
    max_pdu_length = tvb_get_ntohs(tvb, offset) >> 3;
    proto_tree_add_item(tree, hf_fp_hsdsch_max_macd_pdu_len, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset++;

    /* HS-DSCH credits (11 bits) */
    ti = proto_tree_add_bits_ret_val(tree, hf_fp_hsdsch_credits, tvb,
                                     offset*8 + 5, 11, &credits, ENC_BIG_ENDIAN);
    offset += 2;

    /* Interesting values */
    if (credits == 0) {
        proto_item_append_text(ti, " (stop transmission)");
        expert_add_info(pinfo, ti, &ei_fp_stop_hsdpa_transmission);
    }
    if (credits == 2047) {
        proto_item_append_text(ti, " (unlimited)");
    }

    /* HS-DSCH Interval */
    interval = tvb_get_guint8(tvb, offset);
    ti = proto_tree_add_uint(tree, hf_fp_hsdsch_interval, tvb, offset, 1, interval*10);
    offset++;
    if (interval == 0) {
        proto_item_append_text(ti, " (none of the credits shall be used)");
    }

    /* HS-DSCH Repetition period */
    repetition_period = tvb_get_guint8(tvb, offset);
    ti = proto_tree_add_item(tree, hf_fp_hsdsch_repetition_period, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;
    if (repetition_period == 0) {
        proto_item_append_text(ti, " (unlimited repetition period)");
    }

    /* Calculated and show effective rate enabled */
    if (credits == 2047) {
        rate_ti = proto_tree_add_item(tree, hf_fp_hsdsch_unlimited_rate, tvb, 0, 0, ENC_NA);
        proto_item_set_generated(rate_ti);
    }
    else {
        if (interval != 0) {
            /* Cast on credits is safe, since we know it won't exceed 10^11 */
            rate_ti = proto_tree_add_uint(tree, hf_fp_hsdsch_calculated_rate, tvb, 0, 0,
                                          (guint16)credits * max_pdu_length * (1000 / (interval*10)));
            proto_item_set_generated(rate_ti);
        }
    }

    col_append_fstr(pinfo->cinfo, COL_INFO,
                    "   Max-PDU-len=%u  Credits=%u  Interval=%u  Rep-Period=%u",
                    max_pdu_length, (guint16)credits, interval, repetition_period);

    return offset;
}

static int
dissect_hsdpa_capacity_allocation_type_2(packet_info *pinfo, proto_tree *tree,
                                         tvbuff_t *tvb, int offset)
{
    proto_item *ti;
    proto_item *rate_ti;
    guint16     max_pdu_length;
    guint8      repetition_period;
    guint8      interval;
    guint16     credits;

    /* Congestion status */
    proto_tree_add_bits_item(tree, hf_fp_congestion_status, tvb,
                            offset*8 + 2, 2, ENC_BIG_ENDIAN);

    /* CmCH-PI */
    proto_tree_add_item(tree, hf_fp_cmch_pi, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;

    /* 5 spare bits follow here */

    /* Max MAC-d/c PDU length (11 bits) */
    max_pdu_length = tvb_get_ntohs(tvb, offset) & 0x7ff;
    proto_tree_add_item(tree, hf_fp_hsdsch_max_macdc_pdu_len, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;

    /* HS-DSCH credits (16 bits) */
    credits = (tvb_get_ntohs(tvb, offset));
    ti = proto_tree_add_uint(tree, hf_fp_hsdsch_credits, tvb,
                             offset, 2, credits);
    offset += 2;

    /* Interesting values */
    if (credits == 0) {
        proto_item_append_text(ti, " (stop transmission)");
        expert_add_info(pinfo, ti, &ei_fp_stop_hsdpa_transmission);
    }
    if (credits == 65535) {
        proto_item_append_text(ti, " (unlimited)");
    }

    /* HS-DSCH Interval */
    interval = tvb_get_guint8(tvb, offset);
    ti = proto_tree_add_uint(tree, hf_fp_hsdsch_interval, tvb, offset, 1, interval*10);
    offset++;
    if (interval == 0) {
        proto_item_append_text(ti, " (none of the credits shall be used)");
    }

    /* HS-DSCH Repetition period */
    repetition_period = tvb_get_guint8(tvb, offset);
    ti = proto_tree_add_item(tree, hf_fp_hsdsch_repetition_period, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;
    if (repetition_period == 0) {
        proto_item_append_text(ti, " (unlimited repetition period)");
    }

    /* Calculated and show effective rate enabled */
    if (credits == 65535) {
        rate_ti = proto_tree_add_item(tree, hf_fp_hsdsch_unlimited_rate, tvb, 0, 0, ENC_NA);
        proto_item_set_generated(rate_ti);
    }
    else {
        if (interval != 0) {
            rate_ti = proto_tree_add_uint(tree, hf_fp_hsdsch_calculated_rate, tvb, 0, 0,
                                          credits * max_pdu_length * (1000 / (interval*10)));
            proto_item_set_generated(rate_ti);
        }
    }

    col_append_fstr(pinfo->cinfo, COL_INFO,
                    "   Max-PDU-len=%u  Credits=%u  Interval=%u  Rep-Period=%u",
                    max_pdu_length, credits, interval, repetition_period);

    return offset;
}



static int
dissect_common_dynamic_pusch_assignment(packet_info *pinfo, proto_tree *tree,
                                        tvbuff_t *tvb, int offset)
{
    guint8 pusch_set_id;
    guint8 activation_cfn;
    guint8 duration;

    /* PUSCH Set Id */
    pusch_set_id = tvb_get_guint8(tvb, offset);
    proto_tree_add_item(tree, hf_fp_pusch_set_id, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;

    /* Activation CFN */
    activation_cfn = tvb_get_guint8(tvb, offset);
    proto_tree_add_item(tree, hf_fp_activation_cfn, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;

    /* Duration */
    duration = tvb_get_guint8(tvb, offset) * 10;
    proto_tree_add_uint(tree, hf_fp_duration, tvb, offset, 1, duration);
    offset++;

    col_append_fstr(pinfo->cinfo, COL_INFO,
                    "   PUSCH Set Id=%u  Activation CFN=%u  Duration=%u",
                    pusch_set_id, activation_cfn, duration);

    return offset;
}





/* Dissect the control part of a common channel message */
static void
dissect_common_control(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                       int offset, struct fp_info *p_fp_info)
{
    /* Common control frame type */
    guint8 control_frame_type = tvb_get_guint8(tvb, offset);
    proto_tree_add_item(tree, hf_fp_common_control_frame_type, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO,
                   val_to_str_const(control_frame_type, common_control_frame_type_vals, "Unknown"));

    /* Frame-type specific dissection */
    switch (control_frame_type) {
        case COMMON_OUTER_LOOP_POWER_CONTROL:
            /*offset =*/ dissect_common_outer_loop_power_control(pinfo, tree, tvb, offset, p_fp_info);
            break;
        case COMMON_TIMING_ADJUSTMENT:
            /*offset =*/ dissect_common_timing_adjustment(pinfo, tree, tvb, offset, p_fp_info);
            break;
        case COMMON_DL_SYNCHRONISATION:
            /*offset =*/ dissect_common_dl_synchronisation(pinfo, tree, tvb, offset, p_fp_info);
            break;
        case COMMON_UL_SYNCHRONISATION:
            /*offset =*/ dissect_common_ul_synchronisation(pinfo, tree, tvb, offset, p_fp_info);
            break;
        case COMMON_DL_NODE_SYNCHRONISATION:
            /*offset =*/ dissect_common_dl_node_synchronisation(pinfo, tree, tvb, offset);
            break;
        case COMMON_UL_NODE_SYNCHRONISATION:
            /*offset =*/ dissect_common_ul_node_synchronisation(pinfo, tree, tvb, offset);
            break;
        case COMMON_DYNAMIC_PUSCH_ASSIGNMENT:
            /*offset =*/ dissect_common_dynamic_pusch_assignment(pinfo, tree, tvb, offset);
            break;
        case COMMON_TIMING_ADVANCE:
            /*offset =*/ dissect_common_timing_advance(pinfo, tree, tvb, offset);
            break;
        case COMMON_HS_DSCH_Capacity_Request:
            /*offset =*/ dissect_hsdpa_capacity_request(pinfo, tree, tvb, offset);
            break;
        case COMMON_HS_DSCH_Capacity_Allocation:
            /*offset =*/ dissect_hsdpa_capacity_allocation(pinfo, tree, tvb, offset, p_fp_info);
            break;
        case COMMON_HS_DSCH_Capacity_Allocation_Type_2:
            /*offset =*/ dissect_hsdpa_capacity_allocation_type_2(pinfo, tree, tvb, offset);
            break;

        default:
            break;
    }

     /* There is no Spare Extension nor payload crc in common control!? */
   /* dissect_spare_extension_and_crc(tvb, pinfo, tree, 0, offset);
    */
}



/**************************/
/* Dissect a RACH channel */
static void
dissect_rach_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                          int offset, struct fp_info *p_fp_info, void *data)
{
    gboolean is_control_frame;
    guint32 header_crc = 0;
    proto_item * header_crc_pi = NULL;
    guint header_length = 0;

    /* Header CRC */
    header_crc_pi = proto_tree_add_item_ret_uint(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN, &header_crc);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        dissect_common_control(tvb, pinfo, tree, offset, p_fp_info);
        /* For control frame the header CRC is actually frame CRC covering all
         * bytes except the first */
        if (preferences_header_checksum) {
            verify_control_frame_crc(tvb, pinfo, header_crc_pi, (guint16)header_crc);
        }
    }
    else {
        guint8      cfn;
        guint32     encoded;
        guint32     propagation_delay                    = 0;
        proto_item *propagation_delay_ti                 = NULL;
        guint32     received_sync_ul_timing_deviation    = 0;
        proto_item *received_sync_ul_timing_deviation_ti = NULL;
        proto_item *rx_timing_deviation_ti               = NULL;
        guint16     rx_timing_deviation                  = 0;

        /* DATA */

        /* CFN */
        cfn = tvb_get_guint8(tvb, offset);
        proto_tree_add_item(tree, hf_fp_cfn, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        col_append_fstr(pinfo->cinfo, COL_INFO, "CFN=%03u ", cfn);

        /* TFI */
        proto_tree_add_item(tree, hf_fp_tfi, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        if (p_fp_info->channel == CHANNEL_RACH_FDD) {
            /* Propagation delay */
            encoded = tvb_get_guint8(tvb, offset);
            propagation_delay = encoded * 3;
            propagation_delay_ti = proto_tree_add_uint_format(tree, hf_fp_propagation_delay, tvb, offset, 1,
                                               propagation_delay, "Propagation Delay: %u chips (%u)",
                                               propagation_delay, encoded);
            offset++;
        }

        /* Should be TDD 3.84 or 7.68 */
        if (p_fp_info->channel == CHANNEL_RACH_TDD) {
            /* Rx Timing Deviation */
            rx_timing_deviation = tvb_get_guint8(tvb, offset);
            rx_timing_deviation_ti = proto_tree_add_item(tree, hf_fp_rx_timing_deviation, tvb, offset, 1, ENC_BIG_ENDIAN);
            offset++;
        }

        if (p_fp_info->channel == CHANNEL_RACH_TDD_128) {
            /* Received SYNC UL Timing Deviation */
            received_sync_ul_timing_deviation = tvb_get_guint8(tvb, offset);
            received_sync_ul_timing_deviation_ti =
                 proto_tree_add_item(tree, hf_fp_received_sync_ul_timing_deviation, tvb, offset, 1, ENC_BIG_ENDIAN);
            offset++;
        }

        header_length = offset;

        /* TB data */
        offset = dissect_tb_data(tvb, pinfo, tree, offset, p_fp_info, &mac_fdd_rach_handle, data);

        /* CRCIs */
        offset = dissect_crci_bits(tvb, pinfo, tree, p_fp_info, offset);

        /* Info introduced in R6 */
        /* only check if it looks as if they are present */
        if (((p_fp_info->release == 6) || (p_fp_info->release == 7)) &&
            (tvb_reported_length_remaining(tvb, offset) > 2))
        {
            int n;
            guint8 flags;
            /* guint8 flag_bytes = 0; */

            gboolean cell_portion_id_present                 = FALSE;
            gboolean ext_propagation_delay_present           = FALSE;
            gboolean angle_of_arrival_present                = FALSE;
            gboolean ext_rx_sync_ul_timing_deviation_present = FALSE;
            gboolean ext_rx_timing_deviation_present         = FALSE;

            /* New IE flags (assume mandatory for now) */
            do {
                proto_item *new_ie_flags_ti;
                proto_tree *new_ie_flags_tree;
                guint ies_found = 0;

                /* Add new IE flags subtree */
                new_ie_flags_ti = proto_tree_add_string_format(tree, hf_fp_rach_new_ie_flags, tvb, offset, 1,
                                                              "", "New IE flags");
                new_ie_flags_tree = proto_item_add_subtree(new_ie_flags_ti, ett_fp_rach_new_ie_flags);

                /* Read next byte */
                flags = tvb_get_guint8(tvb, offset);
                /* flag_bytes++ */

                /* Dissect individual bits */
                for (n=0; n < 8; n++) {
                    switch (n) {
                        case 6:
                            switch (p_fp_info->division) {
                                case Division_FDD:
                                    /* Ext propagation delay */
                                    ext_propagation_delay_present = TRUE;
                                    proto_tree_add_item(new_ie_flags_tree, hf_fp_rach_ext_propagation_delay_present,
                                                        tvb, offset, 1, ENC_BIG_ENDIAN);
                                    break;
                                case Division_TDD_128:
                                    /* Ext Rx Sync UL Timing */
                                    ext_rx_sync_ul_timing_deviation_present = TRUE;
                                    proto_tree_add_item(new_ie_flags_tree, hf_fp_rach_ext_rx_sync_ul_timing_deviation_present,
                                                        tvb, offset, 1, ENC_BIG_ENDIAN);

                                    break;
                                default:
                                    /* Not defined */
                                    proto_tree_add_item(new_ie_flags_tree, hf_fp_rach_new_ie_flag_unused[6],
                                                        tvb, offset, 1, ENC_BIG_ENDIAN);
                                    break;
                            }
                            break;
                        case 7:
                            switch (p_fp_info->division) {
                                case Division_FDD:
                                    /* Cell Portion ID */
                                    cell_portion_id_present = TRUE;
                                    proto_tree_add_item(new_ie_flags_tree, hf_fp_rach_cell_portion_id_present,
                                                        tvb, offset, 1, ENC_BIG_ENDIAN);
                                    break;
                                case Division_TDD_128:
                                    /* AOA */
                                    angle_of_arrival_present = TRUE;
                                    proto_tree_add_item(new_ie_flags_tree, hf_fp_rach_angle_of_arrival_present,
                                                        tvb, offset, 1, ENC_BIG_ENDIAN);
                                    break;
                                case Division_TDD_384:
                                case Division_TDD_768:
                                    /* Extended Rx Timing Deviation */
                                    ext_rx_timing_deviation_present = TRUE;
                                    proto_tree_add_item(new_ie_flags_tree, hf_fp_rach_ext_rx_timing_deviation_present,
                                                        tvb, offset, 1, ENC_BIG_ENDIAN);
                                    break;
                            }
                            break;

                        default:
                            /* No defined meanings */
                            /* Visual Studio Code Analyzer wrongly thinks n can be 7 here. It can't */
                            proto_tree_add_item(new_ie_flags_tree, hf_fp_rach_new_ie_flag_unused[n],
                                                tvb, offset, 1, ENC_BIG_ENDIAN);
                            break;
                    }
                    if ((flags >> (7-n)) & 0x01) {
                        ies_found++;
                    }
                }
                offset++;

                proto_item_append_text(new_ie_flags_ti, " (%u IEs found)", ies_found);

                /* Last bit set will indicate another flags byte follows... */
            } while (0); /*((flags & 0x01) && (flag_bytes < 31));*/

            /* Cell Portion ID */
            if (cell_portion_id_present) {
                    proto_tree_add_item(tree, hf_fp_cell_portion_id, tvb, offset, 1, ENC_BIG_ENDIAN);
                    offset++;
            }

            /* Ext Rx Timing Deviation */
            if (ext_rx_timing_deviation_present) {
                guint8 extra_bits;
                guint bits_to_extend;
                switch (p_fp_info->division) {
                    case Division_TDD_384:
                        bits_to_extend = 1;
                        break;
                    case Division_TDD_768:
                        bits_to_extend = 2;
                        break;

                    default:
                        /* TODO: report unexpected division type */
                        bits_to_extend = 1;
                        break;
                }
                extra_bits = tvb_get_guint8(tvb, offset) &
                                 ((bits_to_extend == 1) ? 0x01 : 0x03);
                rx_timing_deviation = (extra_bits << 8) | (rx_timing_deviation);
                proto_item_append_text(rx_timing_deviation_ti,
                                       " (extended to 0x%x)",
                                       rx_timing_deviation);
                proto_tree_add_bits_item(tree, hf_fp_extended_bits, tvb,
                                         offset*8 + (8-bits_to_extend), bits_to_extend, ENC_BIG_ENDIAN);
                offset++;
            }

            /* Ext propagation delay. */
            if (ext_propagation_delay_present) {
                guint16 extra_bits = tvb_get_ntohs(tvb, offset) & 0x03ff;
                proto_tree_add_item(tree, hf_fp_ext_propagation_delay, tvb, offset, 2, ENC_BIG_ENDIAN);

                /* Adding 10 bits to original 8 */
                proto_item_append_text(propagation_delay_ti, " (extended to %u)",
                                       ((extra_bits << 8) | propagation_delay) * 3);
                offset += 2;
            }

            /* Angle of Arrival (AOA) */
            if (angle_of_arrival_present) {
                proto_tree_add_item(tree, hf_fp_angle_of_arrival, tvb, offset, 2, ENC_BIG_ENDIAN);
                offset += 2;
            }

            /* Ext. Rx Sync UL Timing Deviation */
            if (ext_rx_sync_ul_timing_deviation_present) {
                guint16 extra_bits;

                /* Ext received Sync UL Timing Deviation */
                extra_bits = tvb_get_ntohs(tvb, offset) & 0x1fff;
                proto_tree_add_item(tree, hf_fp_ext_received_sync_ul_timing_deviation, tvb, offset, 2, ENC_BIG_ENDIAN);

                /* Adding 13 bits to original 8 */
                proto_item_append_text(received_sync_ul_timing_deviation_ti, " (extended to %u)",
                                       (extra_bits << 8) | received_sync_ul_timing_deviation);
                offset += 2;
            }
        }
        if (preferences_header_checksum) {
            verify_header_crc(tvb, pinfo, header_crc_pi, header_crc, header_length);
        }
        /* Spare Extension and Payload CRC */
        dissect_spare_extension_and_crc(tvb, pinfo, tree, 1, offset, header_length);
    }
}


/**************************/
/* Dissect a FACH channel */
static void
dissect_fach_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                          int offset, struct fp_info *p_fp_info, void *data)
{
    gboolean is_control_frame;
    guint32 header_crc = 0;
    proto_item * header_crc_pi = NULL;
    guint header_length = 0;

    /* Header CRC */
    header_crc_pi = proto_tree_add_item_ret_uint(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN, &header_crc);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        dissect_common_control(tvb, pinfo, tree, offset, p_fp_info);
        /* For control frame the header CRC is actually frame CRC covering all
         * bytes except the first */
        if (preferences_header_checksum) {
            verify_control_frame_crc(tvb, pinfo, header_crc_pi, (guint16)header_crc);
        }
    }
    else {
        guint8 cfn;
        /* DATA */

        /* CFN */
        cfn = tvb_get_guint8(tvb, offset);
        proto_tree_add_item(tree, hf_fp_cfn, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        col_append_fstr(pinfo->cinfo, COL_INFO, "CFN=%03u ", cfn);

        /* TFI */
        proto_tree_add_item(tree, hf_fp_fach_tfi, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        /* Transmit power level */
        proto_tree_add_float(tree, hf_fp_transmit_power_level, tvb, offset, 1,
                             (float)(int)(tvb_get_guint8(tvb, offset)) / 10);
        offset++;
        header_length = offset;

        /* TB data */
        offset = dissect_tb_data(tvb, pinfo, tree, offset, p_fp_info, &mac_fdd_fach_handle, data);

        /* New IE flags (if it looks as though they are present) */
        if ((p_fp_info->release == 7) &&
            (tvb_reported_length_remaining(tvb, offset) > 2)) {

            guint8 flags = tvb_get_guint8(tvb, offset);
            guint8 aoa_present = flags & 0x01;
            offset++;

            if (aoa_present) {
                proto_tree_add_item(tree, hf_fp_angle_of_arrival, tvb, offset, 2, ENC_BIG_ENDIAN);
                offset += 2;
            }
        }
        if (preferences_header_checksum) {
            verify_header_crc(tvb, pinfo, header_crc_pi, header_crc, header_length);
        }
        /* Spare Extension and Payload CRC */
        dissect_spare_extension_and_crc(tvb, pinfo, tree, 1, offset, header_length);
    }
}


/**************************/
/* Dissect a DSCH channel */
static void
dissect_dsch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                          int offset, struct fp_info *p_fp_info)
{
    gboolean is_control_frame;

    /* Header CRC */
    proto_tree_add_item(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        dissect_common_control(tvb, pinfo, tree, offset, p_fp_info);
    }
    else {
        guint32 cfn;
        guint header_length = 0;

        /* DATA */

        /* CFN */
        proto_tree_add_item_ret_uint(tree, hf_fp_cfn, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
        offset++;

        col_append_fstr(pinfo->cinfo, COL_INFO, "CFN=%03u ", cfn);

        /* TFI */
        proto_tree_add_item(tree, hf_fp_tfi, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;


        /* Other fields depend upon release & FDD/TDD settings */
        if (((p_fp_info->release == 99) || (p_fp_info->release == 4)) &&
             (p_fp_info->channel == CHANNEL_DSCH_FDD)) {

            /* Power offset */
            proto_tree_add_float(tree, hf_fp_power_offset, tvb, offset, 1,
                                 (float)(-32.0) +
                                  ((float)(int)(tvb_get_guint8(tvb, offset)) * (float)(0.25)));
            offset++;

            /* Code number */
            proto_tree_add_item(tree, hf_fp_code_number, tvb, offset, 1, ENC_BIG_ENDIAN);
            offset++;

            /* Spreading Factor (3 bits) */
            proto_tree_add_item(tree, hf_fp_spreading_factor, tvb, offset, 1, ENC_BIG_ENDIAN);

            /* MC info (4 bits)*/
            proto_tree_add_item(tree, hf_fp_mc_info, tvb, offset, 1, ENC_BIG_ENDIAN);

            /* Last bit of this byte is spare */
            offset++;
        }
        else {
            /* Normal case */

            /* PDSCH Set Id */
            proto_tree_add_item(tree, hf_fp_pdsch_set_id, tvb, offset, 1, ENC_BIG_ENDIAN);
            offset++;

            /* Transmit power level */
            proto_tree_add_float(tree, hf_fp_transmit_power_level, tvb, offset, 1,
                                 (float)(int)(tvb_get_guint8(tvb, offset)) / 10);
            offset++;
        }
        header_length = offset;
        /* TB data */
        offset = dissect_tb_data(tvb, pinfo, tree, offset, p_fp_info, NULL, NULL);

        /* Spare Extension and Payload CRC */
        dissect_spare_extension_and_crc(tvb, pinfo, tree, 1, offset, header_length);
    }
}


/**************************/
/* Dissect a USCH channel */
static void
dissect_usch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                          int offset, struct fp_info *p_fp_info)
{
    gboolean is_control_frame;

    /* Header CRC */
    proto_tree_add_item(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        dissect_common_control(tvb, pinfo, tree, offset, p_fp_info);
    }
    else {
        guint cfn;
        guint16 rx_timing_deviation;
        proto_item *rx_timing_deviation_ti;
        guint header_length = 0;

        /* DATA */

        /* CFN */
        proto_tree_add_item_ret_uint(tree, hf_fp_cfn, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
        offset++;

        col_append_fstr(pinfo->cinfo, COL_INFO, "CFN=%03u ", cfn);

        /* TFI */
        proto_tree_add_item(tree, hf_fp_usch_tfi, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        /* Rx Timing Deviation */
        rx_timing_deviation = tvb_get_guint8(tvb, offset);
        rx_timing_deviation_ti = proto_tree_add_item(tree, hf_fp_rx_timing_deviation,
                                                     tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;
        header_length = offset;
        /* TB data */
        offset = dissect_tb_data(tvb, pinfo, tree, offset, p_fp_info, NULL, NULL);

        /* QE */
        proto_tree_add_item(tree, hf_fp_quality_estimate, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        /* CRCIs */
        offset = dissect_crci_bits(tvb, pinfo, tree, p_fp_info, offset);

        /* New IEs */
        if ((p_fp_info->release == 7) &&
            (tvb_reported_length_remaining(tvb, offset) > 2)) {

            guint8 flags = tvb_get_guint8(tvb, offset);
            guint8 bits_extended = flags & 0x01;
            offset++;

            if (bits_extended) {
                guint8 extra_bits = tvb_get_guint8(tvb, offset) & 0x03;
                proto_item_append_text(rx_timing_deviation_ti,
                                       " (extended to %u)",
                                       (rx_timing_deviation << 2) | extra_bits);
            }
            offset++;
        }

        /* Spare Extension and Payload CRC */
        dissect_spare_extension_and_crc(tvb, pinfo, tree, 1, offset, header_length);
    }
}



/**************************/
/* Dissect a PCH channel  */
static void
dissect_pch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                         int offset, struct fp_info *p_fp_info, void *data)
{
    gboolean is_control_frame;
    guint16  pch_cfn;
    guint32  tfi;
    gboolean paging_indication;
    guint32 header_crc = 0;
    proto_item * header_crc_pi = NULL;

    /* Header CRC */
    header_crc_pi = proto_tree_add_item_ret_uint(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN, &header_crc);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        dissect_common_control(tvb, pinfo, tree, offset, p_fp_info);
        /* For control frame the header CRC is actually frame CRC covering all
         * bytes except the first */
        if (preferences_header_checksum) {
            verify_control_frame_crc(tvb, pinfo, header_crc_pi, (guint16)header_crc);
        }
    }
    else {
        guint header_length = 0;
        /* DATA */

        /* 12-bit CFN value */
        proto_tree_add_item(tree, hf_fp_pch_cfn, tvb, offset, 2, ENC_BIG_ENDIAN);
        pch_cfn = (tvb_get_ntohs(tvb, offset) & 0xfff0) >> 4;
        offset++;

        col_append_fstr(pinfo->cinfo, COL_INFO, "CFN=%04u ", pch_cfn);

        /* Paging indication */
        proto_tree_add_item(tree, hf_fp_pch_pi, tvb, offset, 1, ENC_BIG_ENDIAN);
        paging_indication = tvb_get_guint8(tvb, offset) & 0x01;
        offset++;

        /* 5-bit TFI */
        proto_tree_add_item_ret_uint(tree, hf_fp_pch_tfi, tvb, offset, 1, ENC_BIG_ENDIAN, &tfi);
        offset++;
        header_length = offset;
        /* Optional paging indications */
        if (paging_indication) {
            proto_item *ti;
            ti = proto_tree_add_item(tree, hf_fp_paging_indication_bitmap, tvb,
                                     offset,
                                     (p_fp_info->paging_indications+7) / 8,
                                     ENC_NA);
            proto_item_append_text(ti, " (%u bits)", p_fp_info->paging_indications);

            if(preferences_track_paging_indications && !PINFO_FD_VISITED(pinfo)){
                paging_indications_info_t* current_pi_info;
                current_pi_info = wmem_new0(wmem_file_scope(), paging_indications_info_t);
                current_pi_info->frame_number = pinfo->num;
                current_pi_info->paging_indications_bitmap = (guint8*)tvb_memdup(wmem_file_scope(), tvb, offset, (p_fp_info->paging_indications+7) / 8);
                p_fp_info->current_paging_indications = current_pi_info;
            }

            offset += ((p_fp_info->paging_indications+7) / 8);
        }
        if(preferences_track_paging_indications) {
            if(p_fp_info->relevant_paging_indications) {
                /*If tracking PI is enabled and PI info (from the last packet) is attached, show on tree*/
                proto_item *ti;
                proto_tree *relevant_pi_tree;

                tvbuff_t *pi_tvb;
                pi_tvb = tvb_new_child_real_data(tvb,
                                                 p_fp_info->relevant_paging_indications->paging_indications_bitmap,
                                                 (p_fp_info->paging_indications+7) / 8,
                                                 (p_fp_info->paging_indications+7) / 8);
                add_new_data_source(pinfo, pi_tvb, "Relevant Paging Indication");
                ti = proto_tree_add_item(tree, hf_fp_relevant_paging_indication_bitmap, pi_tvb,
                                         0,
                                         (p_fp_info->paging_indications+7) / 8,
                                         ENC_NA);
                proto_item_append_text(ti, " (%u bits)", p_fp_info->paging_indications);
                proto_item_set_generated(ti);
                relevant_pi_tree = proto_item_add_subtree(ti, ett_fp_pch_relevant_pi);
                ti = proto_tree_add_uint(relevant_pi_tree, hf_fp_relevant_pi_frame,
                                                           tvb, 0, 0, p_fp_info->relevant_paging_indications->frame_number);
                proto_item_set_generated(ti);
            }
            else {
                /* PI info not attached. Check if this frame has any Transport Blocks (i.e. RRC payloads) */
                if(tfi > 0)
                {
                    /* This frame has RRC payload(s) but the PI info is missing, report to the user*/
                    proto_tree_add_expert(tree, pinfo, &ei_fp_pch_lost_relevant_pi_frame, tvb, offset, -1);
                }
            }
        }

        /* TB data */
        offset = dissect_tb_data(tvb, pinfo, tree, offset, p_fp_info, &mac_fdd_pch_handle, data);

        if (preferences_header_checksum) {
            verify_header_crc(tvb, pinfo, header_crc_pi, header_crc, header_length);
        }
        /* Spare Extension and Payload CRC */
        dissect_spare_extension_and_crc(tvb, pinfo, tree, 1, offset, header_length);
    }
}


/**************************/
/* Dissect a CPCH channel */
static void
dissect_cpch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                          int offset, struct fp_info *p_fp_info)
{
    gboolean is_control_frame;

    /* Header CRC */
    proto_tree_add_item(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        dissect_common_control(tvb, pinfo, tree, offset, p_fp_info);
    }
    else {
        guint cfn;
        guint32 encoded;
        guint header_length = 0;
        guint32 propagation_delay = 0;

        /* DATA */

        /* CFN */
        proto_tree_add_item_ret_uint(tree, hf_fp_cfn, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
        offset++;

        col_append_fstr(pinfo->cinfo, COL_INFO, "CFN=%03u ", cfn);

        /* TFI */
        proto_tree_add_item(tree, hf_fp_cpch_tfi, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        /* Propagation delay */
        encoded = tvb_get_guint8(tvb, offset);
        propagation_delay = encoded * 3;
        proto_tree_add_uint_format_value(tree, hf_fp_propagation_delay, tvb, offset, 1,
                                               propagation_delay, "Propagation Delay: %u chips (%u)",
                                               propagation_delay, encoded);
        offset++;
        header_length = offset; /* XXX this might be wrong */
        /* TB data */
        offset = dissect_tb_data(tvb, pinfo, tree, offset, p_fp_info, NULL, NULL);

        /* CRCIs */
        offset = dissect_crci_bits(tvb, pinfo, tree, p_fp_info, offset);

        /* Spare Extension and Payload CRC */
        dissect_spare_extension_and_crc(tvb, pinfo, tree, 1, offset, header_length);
    }
}


/**************************/
/* Dissect a BCH channel  */
static void
dissect_bch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                         int offset, struct fp_info *p_fp_info)
{
    gboolean is_control_frame;

    /* Header CRC */
    proto_tree_add_item(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        dissect_common_control(tvb, pinfo, tree, offset, p_fp_info);
    }
}


/********************************/
/* Dissect an IUR DSCH channel  */
static void
dissect_iur_dsch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                              int offset, struct fp_info *p_fp_info)
{
    gboolean is_control_frame;

    /* Header CRC */
    proto_tree_add_item(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        dissect_common_control(tvb, pinfo, tree, offset, p_fp_info);
    }
    else {
        /* TODO: DATA */
    }
}




/************************/
/* DCH control messages */

static int
dissect_dch_timing_adjustment(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset)
{
    guint32     cfn;
    gint16      toa;
    proto_item *toa_ti;

    /* CFN control */
    proto_tree_add_item_ret_uint(tree, hf_fp_cfn_control, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
    offset++;

    /* ToA */
    toa = tvb_get_ntohs(tvb, offset);
    toa_ti = proto_tree_add_item(tree, hf_fp_toa, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;

    expert_add_info_format(pinfo, toa_ti, &ei_fp_timing_adjustmentment_reported, "Timing adjustmentment reported (%.3f ms)", ((float)(toa) / 8));

    col_append_fstr(pinfo->cinfo, COL_INFO,
                    " CFN = %u, ToA = %d", cfn, toa);

    return offset;
}

static int
dissect_dch_rx_timing_deviation(packet_info *pinfo, proto_tree *tree,
                                tvbuff_t *tvb, int offset,
                                struct fp_info *p_fp_info)
{
    guint16     timing_deviation;
    gint        timing_deviation_chips;
    proto_item *timing_deviation_ti;

    /* CFN control */
    proto_tree_add_item(tree, hf_fp_cfn_control, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;

    /* Rx Timing Deviation */
    timing_deviation = tvb_get_guint8(tvb, offset);
    timing_deviation_ti = proto_tree_add_item(tree, hf_fp_dch_rx_timing_deviation, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;

    /* May be extended in R7, but in this case there are at least 2 bytes remaining */
    if ((p_fp_info->release == 7) &&
        (tvb_reported_length_remaining(tvb, offset) >= 2)) {

        /* New IE flags */
        guint64 extended_bits_present;
        guint64 e_rucch_present;

        /* Read flags */
        proto_tree_add_bits_ret_val(tree, hf_fp_e_rucch_present, tvb,
                                    offset*8 + 6, 1, &e_rucch_present, ENC_BIG_ENDIAN);
        proto_tree_add_bits_ret_val(tree, hf_fp_extended_bits_present, tvb,
                                    offset*8 + 7, 1, &extended_bits_present, ENC_BIG_ENDIAN);
        offset++;

        /* Optional E-RUCCH */
        if (e_rucch_present) {

            /* Value of bit_offset depends upon division type */
            int bit_offset;

            switch (p_fp_info->division) {
                case Division_TDD_384:
                    bit_offset = 6;
                    break;
                case Division_TDD_768:
                    bit_offset = 5;
                    break;
                default:
                    {
                        proto_tree_add_expert(tree, pinfo, &ei_fp_expecting_tdd, tvb, 0, 0);
                        bit_offset = 6;
                    }
            }

            proto_tree_add_item(tree, hf_fp_dch_e_rucch_flag, tvb, offset, 1, ENC_BIG_ENDIAN);
            proto_tree_add_bits_item(tree, hf_fp_dch_e_rucch_flag, tvb,
                                     offset*8 + bit_offset, 1, ENC_BIG_ENDIAN);
        }

        /* Timing deviation may be extended by another:
           - 1 bits (3.84 TDD)    OR
           - 2 bits (7.68 TDD)
        */
        if (extended_bits_present) {
            guint8 extra_bits;
            guint bits_to_extend;
            switch (p_fp_info->division) {
                case Division_TDD_384:
                    bits_to_extend = 1;
                    break;
                case Division_TDD_768:
                    bits_to_extend = 2;
                    break;

                default:
                    /* TODO: report unexpected division type */
                    bits_to_extend = 1;
                    break;
            }
            extra_bits = tvb_get_guint8(tvb, offset) &
                             ((bits_to_extend == 1) ? 0x01 : 0x03);
            timing_deviation = (extra_bits << 8) | (timing_deviation);
            proto_item_append_text(timing_deviation_ti,
                                   " (extended to 0x%x)",
                                   timing_deviation);
            proto_tree_add_bits_item(tree, hf_fp_extended_bits, tvb,
                                     offset*8 + (8-bits_to_extend), bits_to_extend, ENC_BIG_ENDIAN);
            offset++;
        }
    }

    timing_deviation_chips = (timing_deviation*4) - 1024;
    proto_item_append_text(timing_deviation_ti, " (%d chips)",
                           timing_deviation_chips);

    col_append_fstr(pinfo->cinfo, COL_INFO, " deviation = %u (%d chips)",
                    timing_deviation, timing_deviation_chips);

    return offset;
}

static int
dissect_dch_dl_synchronisation(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset)
{
    guint32 cfn;

    /* CFN control */
    proto_tree_add_item_ret_uint(tree, hf_fp_cfn_control, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
    offset++;

    col_append_fstr(pinfo->cinfo, COL_INFO, " CFN = %u", cfn);

    return offset;
}

static int
dissect_dch_ul_synchronisation(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset)
{
    guint32 cfn;
    gint16 toa;

    /* CFN control */
    proto_tree_add_item_ret_uint(tree, hf_fp_cfn_control, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
    offset++;

    /* ToA */
    toa = tvb_get_ntohs(tvb, offset);
    proto_tree_add_item(tree, hf_fp_toa, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;

    col_append_fstr(pinfo->cinfo, COL_INFO, " CFN = %u, ToA = %d",
                    cfn, toa);

    return offset;
}

static int
dissect_dch_outer_loop_power_control(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset)
{
    /* UL SIR target */
    guint8 encoded = tvb_get_guint8(tvb, offset);
    float target = (float)-8.2 + ((float)0.1 * (float)(int)(encoded));
    proto_tree_add_float_format_value(tree, hf_fp_ul_sir_target, tvb, offset, 1, target, "%.1f dB (%u)", target, encoded);
    offset++;

    col_append_fstr(pinfo->cinfo, COL_INFO, " UL SIR Target = %.1f", target);

    return offset;
}

static int
dissect_dch_dl_node_synchronisation(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset)
{
    return dissect_common_dl_node_synchronisation(pinfo, tree, tvb, offset);
}

static int
dissect_dch_ul_node_synchronisation(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset)
{
    return dissect_common_ul_node_synchronisation(pinfo, tree, tvb, offset);
}

static int
dissect_dch_radio_interface_parameter_update(proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
{
    float tpc_po;
    gint8 max_tx_pwr;
    int    n;
    guint8 encoded;

    /* Show defined flags in these 2 bytes */
    for (n=4; n >= 0; n--) {
        proto_tree_add_item(tree, hf_fp_radio_interface_parameter_update_flag[n], tvb, offset, 2, ENC_BIG_ENDIAN);
    }
    offset += 2;

    /* CFN  */
    proto_tree_add_item(tree, hf_fp_cfn, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;

    /* DPC mode */
    proto_tree_add_item(tree, hf_fp_dpc_mode, tvb, offset, 1, ENC_BIG_ENDIAN);

    /* TPC PO */
    encoded = tvb_get_guint8(tvb, offset) & 0x1f;
    tpc_po = (float)encoded * 0.25f;
    proto_tree_add_float_format_value(tree, hf_fp_tpc_po, tvb, offset, 1, tpc_po,
                                      "%.2f dB (%u)", tpc_po, encoded);
    offset++;

    /* Multiple RL sets indicator */
    proto_tree_add_item(tree, hf_fp_multiple_rl_set_indicator, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 2;

    /* Maximum UE TX Power */
    encoded = tvb_get_guint8(tvb, offset) & 0x7f;
    max_tx_pwr = -55 + encoded;
    proto_tree_add_int_format(tree, hf_fp_max_ue_tx_pow, tvb, offset, 1, max_tx_pwr,
                              "%d dBm (%u)", max_tx_pwr, encoded);
    offset++;

    return offset;
}

static int
dissect_dch_timing_advance(proto_tree *tree, packet_info *pinfo,
                           tvbuff_t *tvb, int offset, struct fp_info *p_fp_info)
{
    guint32     cfn;
    guint16     timing_advance;
    proto_item *timing_advance_ti;

    /* CFN control */
    proto_tree_add_item_ret_uint(tree, hf_fp_cfn_control, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
    offset++;

    /* Timing Advance */
    timing_advance = (tvb_get_guint8(tvb, offset) & 0x3f) * 4;
    timing_advance_ti = proto_tree_add_uint(tree, hf_fp_timing_advance, tvb, offset, 1, timing_advance);
    offset++;

    if ((p_fp_info->release == 7) &&
        (tvb_reported_length_remaining(tvb, offset) > 0)) {

        /* New IE flags */
        guint8 flags = tvb_get_guint8(tvb, offset);
        guint8 extended_bits = flags & 0x01;
        offset++;

        if (extended_bits) {
            guint8 extra_bit = tvb_get_guint8(tvb, offset) & 0x01;
            proto_item_append_text(timing_advance_ti, " (extended to %u)",
                                   (timing_advance << 1) | extra_bit);
        }
        offset++;
    }

    col_append_fstr(pinfo->cinfo, COL_INFO, " CFN = %u, TA = %u",
                    cfn, timing_advance);

    return offset;
}

static int
dissect_dch_tnl_congestion_indication(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset)
{
    guint64 status;

    /* Congestion status */
    proto_tree_add_bits_ret_val(tree, hf_fp_congestion_status, tvb,
                                offset*8 + 6, 2, &status, ENC_BIG_ENDIAN);
    offset++;

    col_append_fstr(pinfo->cinfo, COL_INFO, " status = %s",
                    val_to_str_const((guint16)status, congestion_status_vals, "unknown"));

    return offset;
}




/* DCH control frame */
static void
dissect_dch_control_frame(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb,
                          int offset, struct fp_info *p_fp_info)
{
    /* Control frame type */
    guint8 control_frame_type = tvb_get_guint8(tvb, offset);
    proto_tree_add_item(tree, hf_fp_dch_control_frame_type, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO,
                   val_to_str_const(control_frame_type,
                                    dch_control_frame_type_vals, "Unknown"));

    switch (control_frame_type) {
        case DCH_TIMING_ADJUSTMENT:
            /*offset =*/ dissect_dch_timing_adjustment(tree, pinfo, tvb, offset);
            break;
        case DCH_RX_TIMING_DEVIATION:
            /*offset =*/ dissect_dch_rx_timing_deviation(pinfo, tree, tvb, offset, p_fp_info);
            break;
        case DCH_DL_SYNCHRONISATION:
            /*offset =*/ dissect_dch_dl_synchronisation(tree, pinfo, tvb, offset);
            break;
        case DCH_UL_SYNCHRONISATION:
            /*offset =*/ dissect_dch_ul_synchronisation(tree, pinfo, tvb, offset);
            break;
        case DCH_OUTER_LOOP_POWER_CONTROL:
            /*offset =*/ dissect_dch_outer_loop_power_control(tree, pinfo, tvb, offset);
            break;
        case DCH_DL_NODE_SYNCHRONISATION:
            /*offset =*/ dissect_dch_dl_node_synchronisation(tree, pinfo, tvb, offset);
            break;
        case DCH_UL_NODE_SYNCHRONISATION:
            /*offset =*/ dissect_dch_ul_node_synchronisation(tree, pinfo, tvb, offset);
            break;
        case DCH_RADIO_INTERFACE_PARAMETER_UPDATE:
            /*offset =*/ dissect_dch_radio_interface_parameter_update(tree, pinfo, tvb, offset);
            break;
        case DCH_TIMING_ADVANCE:
            /*offset =*/ dissect_dch_timing_advance(tree, pinfo, tvb, offset, p_fp_info);
            break;
        case DCH_TNL_CONGESTION_INDICATION:
            /*offset =*/ dissect_dch_tnl_congestion_indication(tree, pinfo, tvb, offset);
            break;
    }

    /* Spare Extension */
   /* dissect_spare_extension_and_crc(tvb, pinfo, tree, 0, offset);
    */
}

/*******************************/
/* Dissect a DCH channel       */
static void
dissect_dch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                         int offset, struct fp_info *p_fp_info, void *data)
{
    gboolean is_control_frame;
    guint32   cfn;
    guint header_length = 0;
    guint32 header_crc = 0;
    proto_item * header_crc_pi = NULL;

    /* Header CRC */
    header_crc_pi = proto_tree_add_item_ret_uint(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN, &header_crc);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO,
                   is_control_frame ? " [Control] " :
                                      ((p_fp_info->is_uplink) ? " [ULData] " :
                                                                " [DLData] " ));

    if (is_control_frame) {
        /* DCH control frame */
        dissect_dch_control_frame(tree, pinfo, tvb, offset, p_fp_info);
        /* For control frame the header CRC is actually frame CRC covering all
         * bytes except the first */
        if (preferences_header_checksum) {
            verify_control_frame_crc(tvb, pinfo, header_crc_pi, (guint16)header_crc);
        }
    } else {
        /************************/
        /* DCH data here        */
        int chan;
        /* CFN */
        proto_tree_add_item_ret_uint(tree, hf_fp_cfn, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
        offset++;

        col_append_fstr(pinfo->cinfo, COL_INFO, "CFN=%03u ", cfn);

        /* One TFI for each channel */
        for (chan=0; chan < p_fp_info->num_chans; chan++) {
            proto_tree_add_item(tree, hf_fp_tfi, tvb, offset, 1, ENC_BIG_ENDIAN);
            offset++;
        }
        header_length = offset;
        /* Dissect TB data */
        offset = dissect_tb_data(tvb, pinfo, tree, offset, p_fp_info, &mac_fdd_dch_handle, data);

        /* QE (uplink only) */
        if (p_fp_info->is_uplink) {
            proto_tree_add_item(tree, hf_fp_quality_estimate, tvb, offset, 1, ENC_BIG_ENDIAN);
            offset++;
        }

        /* CRCI bits (uplink only) */
        if (p_fp_info->is_uplink) {
            offset = dissect_crci_bits(tvb, pinfo, tree, p_fp_info, offset);
        }
        if (preferences_header_checksum) {
            verify_header_crc(tvb, pinfo, header_crc_pi, header_crc, header_length);
        }
        /* Spare extension and payload CRC (optional) */
        dissect_spare_extension_and_crc(tvb, pinfo, tree,
                                        p_fp_info->dch_crc_present, offset, header_length);
    }
}

/**********************************/
/* Dissect an E-DCH channel       */
static void
dissect_e_dch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                           int offset, struct fp_info *p_fp_info,
                           gboolean is_common,
                           void *data)
{
    gboolean is_control_frame;
    guint8   number_of_subframes;
    guint32  cfn;
    int      n;
    struct   edch_t1_subframe_info subframes[16];
    guint32 header_crc = 0;
    proto_item * header_crc_pi = NULL;
    proto_item * item;
    guint header_length = 0;
    rlc_info * rlcinf;

    if (p_fp_info->edch_type == 1) {
        col_append_str(pinfo->cinfo, COL_INFO, " (T2)");
    }

    /* Header CRC */
     /* the bitmask doesn't properly handle this delicate case, do manually */
    header_crc = (tvb_get_bits8(tvb, offset*8, 7) << 4) + tvb_get_bits8(tvb, offset*8+8, 4);

    /* Frame Type */
    is_control_frame = tvb_get_guint8(tvb, offset) & 0x01;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        /* DCH control frame */

        /* For control frame the header CRC is actually frame CRC covering all
         * bytes except the first */
        header_crc_pi = proto_tree_add_item_ret_uint(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN, &header_crc);
        proto_tree_add_item(tree, hf_fp_ft, tvb, 0, 1, ENC_BIG_ENDIAN);
        offset++;
        if (preferences_header_checksum) {
            verify_control_frame_crc(tvb, pinfo, header_crc_pi, (guint16)header_crc);
        }
        dissect_dch_control_frame(tree, pinfo, tvb, offset, p_fp_info);
    }
    else {
        /********************************/
        /* E-DCH data here              */
        guint  bit_offset = 0;
        guint  total_pdus = 0;
        guint  total_bits = 0;
        gboolean dissected = FALSE;

        rlcinf = (rlc_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0);
        if (!rlcinf) {
            rlcinf = wmem_new0(wmem_packet_scope(), rlc_info);
        }

        header_crc_pi = proto_tree_add_uint_format(tree, hf_fp_edch_header_crc, tvb,
                offset, 2, header_crc,
                "%u%u%u%u %u%u%u. %u%u%u%u .... = E-DCH Header CRC: 0x%x",
                (header_crc >> 10) & 1,
                (header_crc >> 9) & 1,
                (header_crc >> 8) & 1,
                (header_crc >> 7) & 1,
                (header_crc >> 6) & 1,
                (header_crc >> 5) & 1,
                (header_crc >> 4) & 1,
                (header_crc >> 3) & 1,
                (header_crc >> 2) & 1,
                (header_crc >> 1) & 1,
                (header_crc >> 0) & 1, header_crc);
        proto_tree_add_item(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;
        /* FSN */
        proto_tree_add_item(tree, hf_fp_edch_fsn, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        /* Number of subframes.
           This was 3 bits in early releases, is 4 bits offset by 1 in later releases  */
        if ((p_fp_info->release >= 6) &&
            ((p_fp_info->release_year > 2005) ||
             ((p_fp_info->release_year == 2005) && (p_fp_info->release_month >= 9)))) {

            /* Use 4 bits plus offset of 1 */
            number_of_subframes = (tvb_get_guint8(tvb, offset) & 0x0f) + 1;
        }
        else {
            /* Use 3 bits only */
            number_of_subframes = (tvb_get_guint8(tvb, offset) & 0x07);
        }
        proto_tree_add_uint(tree, hf_fp_edch_number_of_subframes, tvb, offset, 1,
                            number_of_subframes);

        offset++;

        /* CFN */
        proto_tree_add_item_ret_uint(tree, hf_fp_cfn, tvb, offset, 1, ENC_BIG_ENDIAN, &cfn);
        offset++;

        /* Remainder of T2 or common data frames differ here... */
        if (p_fp_info->edch_type == 1) {
            dissect_e_dch_t2_or_common_channel_info(tvb, pinfo, tree, offset, p_fp_info,
                                                    number_of_subframes,
                                                    is_common, header_crc,
                                                    header_crc_pi, data);
            return;
        }

        /* EDCH subframe header list */
        for (n=0; n < number_of_subframes; n++) {
            int i;
            int start_offset = offset;
            proto_item *subframe_header_ti;
            proto_tree *subframe_header_tree;

            /* Add subframe header subtree */
            subframe_header_ti = proto_tree_add_string_format(tree, hf_fp_edch_subframe_header, tvb, offset, 0,
                                                              "", "Subframe");
            subframe_header_tree = proto_item_add_subtree(subframe_header_ti, ett_fp_edch_subframe_header);

            /* Number of HARQ Retransmissions */
            proto_tree_add_item(subframe_header_tree, hf_fp_edch_harq_retransmissions, tvb,
                                offset, 1, ENC_BIG_ENDIAN);

            /* Subframe number */
            subframes[n].subframe_number = (tvb_get_guint8(tvb, offset) & 0x07);
            proto_tree_add_bits_item(subframe_header_tree, hf_fp_edch_subframe_number, tvb,
                                     offset*8+5, 3, ENC_BIG_ENDIAN);
            offset++;

            /* Number of MAC-es PDUs */
            subframes[n].number_of_mac_es_pdus = (tvb_get_guint8(tvb, offset) & 0xf0) >> 4;
            proto_tree_add_item(subframe_header_tree, hf_fp_edch_number_of_mac_es_pdus,
                                tvb, offset, 1, ENC_BIG_ENDIAN);
            bit_offset = 4;

            proto_item_append_text(subframe_header_ti, " %u header (%u MAC-es PDUs)",
                                   subframes[n].subframe_number,
                                   subframes[n].number_of_mac_es_pdus);

            /* Details of each MAC-es PDU */
            for (i=0; i < subframes[n].number_of_mac_es_pdus; i++) {
                guint64 ddi;
                guint64 n_pdus;    /*Size of the PDU*/

                proto_item *ddi_ti;
                gint ddi_size = -1;
                int     p;

                /* DDI (6 bits) */
                ddi_ti = proto_tree_add_bits_ret_val(subframe_header_tree, hf_fp_edch_ddi, tvb,
                                                     offset*8 + bit_offset, 6, &ddi, ENC_BIG_ENDIAN);

                rlcinf->rbid[i] = (guint8)ddi;
                /********************************/
                /* Look up data in higher layers*/
                /* Look up the size from this DDI value */
                for (p=0; p < p_fp_info->no_ddi_entries; p++) {
                    if (ddi == p_fp_info->edch_ddi[p]) {
                        ddi_size = p_fp_info->edch_macd_pdu_size[p];

                        break;
                    }
                }

                if (ddi_size == -1) {
                    expert_add_info_format(pinfo, ddi_ti, &ei_fp_ddi_not_defined, "DDI %u not defined for this UE!", (guint)ddi);
                    return;
                }
                else {
                    proto_item_append_text(ddi_ti, " (%d bits)", ddi_size);
                }

                subframes[n].ddi[i] = (guint8)ddi;
                bit_offset += 6;

                /* Number of MAC-d PDUs (6 bits) */
                item = proto_tree_add_bits_ret_val(subframe_header_tree, hf_fp_edch_number_of_mac_d_pdus, tvb,
                                            offset*8 + bit_offset, 6, &n_pdus, ENC_BIG_ENDIAN);
                if (n_pdus > MAX_MAC_FRAMES) {
                    expert_add_info_format(pinfo, item, &ei_fp_invalid_frame_count, "Invalid number of PDUs (max is %u)", MAX_MAC_FRAMES);
                    return;
                }

                subframes[n].number_of_mac_d_pdus[i] = (guint8)n_pdus;
                bit_offset += 6;
            }

            offset += ((bit_offset+7)/8);

            /* Tree should cover entire subframe header */
            proto_item_set_len(subframe_header_ti, offset - start_offset);
        }
        header_length = offset;
        /* EDCH subframes */
        for (n=0; n < number_of_subframes; n++) {
            int i;
            proto_item *subframe_ti;
            proto_tree *subframe_tree;
            guint bits_in_subframe = 0;
            guint mac_d_pdus_in_subframe = 0;
            guint    lchid=0;    /*Logcial channel id*/
            guint32 user_identity;
            umts_mac_info *macinf;
            bit_offset = 0;

            macinf = (umts_mac_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0);
            if (!macinf) {
                macinf = wmem_new0(wmem_packet_scope(), umts_mac_info);
            }
            /* Add subframe subtree */
            subframe_ti = proto_tree_add_string_format(tree, hf_fp_edch_subframe, tvb, offset, 0,
                                                       "", "Subframe %u data", subframes[n].subframe_number);
            subframe_tree = proto_item_add_subtree(subframe_ti, ett_fp_edch_subframe);

            for (i=0; i < subframes[n].number_of_mac_es_pdus; i++) {
                int         m;
                guint16     size = 0;
                /* guint8      tsn; */
                guint       send_size;
                proto_item  *ti;
                int         macd_idx;
                proto_tree  *maces_tree = NULL;

                /** TODO: Merge these two loops? **/
                /* Look up mac-d pdu size for this ddi */
                for (m=0; m < p_fp_info->no_ddi_entries; m++) {
                    if (subframes[n].ddi[i] == p_fp_info->edch_ddi[m]) {
                        size = p_fp_info->edch_macd_pdu_size[m];
                        break;
                    }
                }
                /* Look up logicalchannel id for this DDI value */
                for (m=0; m < p_fp_info->no_ddi_entries; m++) {
                    if (subframes[n].ddi[i] == p_fp_info->edch_ddi[m]) {
                        lchid = p_fp_info->edch_lchId[m];
                        break;
                    }
                }

                if (m == p_fp_info->no_ddi_entries) {
                    /* Not found.  Oops */
                    expert_add_info(pinfo, NULL, &ei_fp_unable_to_locate_ddi_entry);
                    return;
                }

                /* Send MAC-dd PDUs together as one MAC-es PDU */
                send_size = size * subframes[n].number_of_mac_d_pdus[i];

                /* 2 bits spare */
                proto_tree_add_item(subframe_tree, hf_fp_edch_pdu_padding, tvb,
                                    offset + (bit_offset/8),
                                    1, ENC_BIG_ENDIAN);
                bit_offset += 2;

                /* TSN */
                /* tsn = (tvb_get_guint8(tvb, offset + (bit_offset/8)) & 0x3f); */
                proto_tree_add_item(subframe_tree, hf_fp_edch_tsn, tvb,
                                    offset + (bit_offset/8),
                                    1, ENC_BIG_ENDIAN);
                bit_offset += 6;

                /* PDU */
                if (subframe_tree) {
                    ti = proto_tree_add_item(subframe_tree, hf_fp_edch_mac_es_pdu, tvb,
                                             offset + (bit_offset/8),
                                             ((bit_offset % 8) + send_size + 7) / 8,
                                             ENC_NA);
                    proto_item_append_text(ti, " (%u * %u = %u bits, PDU %d)",
                                           size, subframes[n].number_of_mac_d_pdus[i],
                                           send_size, n);
                    maces_tree = proto_item_add_subtree(ti, ett_fp_edch_maces);
                }
                /* Determine the UE ID to use in RLC */
                user_identity = p_fp_info->com_context_id;
                if(p_fp_info->urnti) {
                    user_identity = p_fp_info->urnti;
                }
                for (macd_idx = 0; macd_idx < subframes[n].number_of_mac_d_pdus[i]; macd_idx++) {

                    if (preferences_call_mac_dissectors) {
                        /* Should no longer happen ??*/
                        if (macd_idx >= MAX_MAC_FRAMES) {
                            expert_add_info_format(pinfo, subframe_tree, &ei_fp_invalid_frame_count, "Invalid frame count (max is %u)", MAX_MAC_FRAMES);
                            return;
                        }

                        tvbuff_t *next_tvb;
                        pinfo->fd->subnum = macd_idx; /* set subframe number to current TB */
                        /* create new TVB and pass further on */
                        next_tvb = tvb_new_subset_length(tvb, offset + bit_offset/8,
                                ((bit_offset % 8) + size + 7) / 8);

                        /*Set up information needed for MAC and lower layers*/
                        macinf->content[macd_idx] = lchId_type_table[lchid];     /*Set the proper Content type for the mac layer.*/
                        macinf->lchid[macd_idx] = lchid;
                        rlcinf->mode[macd_idx] = lchId_rlc_map[lchid]; /* Set RLC mode by lchid to RLC_MODE map in nbap.h */

                        /* Set UE ID to U-RNTI or NBAP Comuncation Context*/
                        rlcinf->ueid[macd_idx] = user_identity;
                        rlcinf->rbid[macd_idx] = lchid;
                        rlcinf->li_size[macd_idx] = RLC_LI_7BITS;

                        rlcinf->ciphered[macd_idx] = FALSE;
                        rlcinf->deciphered[macd_idx] = FALSE;
                        p_fp_info->cur_tb = macd_idx;    /*Set the transport block index (NOTE: This and not subnum is used in MAC dissector!)*/

                        call_dissector_with_data(mac_fdd_edch_handle, next_tvb, pinfo, top_level_tree, data);
                        dissected = TRUE;
                    }
                    else {
                        /* Just add as a MAC-d PDU */
                        proto_tree_add_item(maces_tree, hf_fp_mac_d_pdu, tvb,
                                            offset + (bit_offset/8),
                                            ((bit_offset % 8) + size + 7) / 8,
                                            ENC_NA);
                    }
                    bit_offset += size;
                }

                bits_in_subframe += send_size;
                mac_d_pdus_in_subframe += subframes[n].number_of_mac_d_pdus[i];

                /* Pad out to next byte */
                if (bit_offset % 8) {
                    bit_offset += (8 - (bit_offset % 8));
                }
            }

            if (tree) {
                /* Tree should cover entire subframe */
                proto_item_set_len(subframe_ti, bit_offset/8);
                /* Append summary info to subframe label */
                proto_item_append_text(subframe_ti, " (%u bits in %u MAC-d PDUs)",
                                       bits_in_subframe, mac_d_pdus_in_subframe);
            }
            total_pdus += mac_d_pdus_in_subframe;
            total_bits += bits_in_subframe;

            offset += (bit_offset/8);
        }

        /* Report number of subframes in info column
         * do this only if no other dissector was called */
        if (dissected == FALSE) {
            col_append_fstr(pinfo->cinfo, COL_INFO,
                            " CFN = %03u   (%u bits in %u pdus in %u subframes)",
                            cfn, total_bits, total_pdus, number_of_subframes);
        }
        /* Add data summary to info column */
        /*col_append_fstr(pinfo->cinfo, COL_INFO, " (%u bytes in %u SDUs in %u MAC-is PDUs in %u subframes)",
                        total_bytes, macis_sdus_found, macis_pdus, number_of_subframes);*/
        if (preferences_header_checksum) {
            verify_header_crc_edch(tvb, pinfo, header_crc_pi, header_crc, header_length);
        }
        /* Spare extension and payload CRC (optional) */
        dissect_spare_extension_and_crc(tvb, pinfo, tree,
                                        p_fp_info->dch_crc_present, offset, header_length);
    }
}

/* Dissect the remainder of the T2 or common frame that differs from T1 */
static void
dissect_e_dch_t2_or_common_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                        int offset, struct fp_info *p_fp_info,
                                        int number_of_subframes,
                                        gboolean is_common,
                                        guint16 header_crc,
                                        proto_item * header_crc_pi,
                                        void *data)
{
    int      n;
    int      pdu_no;
    guint64  total_macis_sdus;
    guint16  macis_sdus_found = 0;
    guint16  macis_pdus       = 0;
    gboolean F                = TRUE; /* We want to continue loop if get E-RNTI indication... */
    gint     bit_offset;
    proto_item *subframe_macis_descriptors_ti = NULL;
    static struct edch_t2_subframe_info subframes[16];
    guint header_length = 0;
    /* User Buffer size */
    proto_tree_add_bits_item(tree, hf_fp_edch_user_buffer_size, tvb, offset*8,
                             18, ENC_BIG_ENDIAN);
    offset += 2;

    /* Spare is in-between... */

    /* Total number of MAC-is SDUs */
    proto_tree_add_bits_ret_val(tree, hf_fp_edch_no_macid_sdus, tvb, offset*8+4,
                                12, &total_macis_sdus, ENC_BIG_ENDIAN);
    offset += 2;

    if (is_common) {
        /* E-RNTI */
        proto_tree_add_item(tree, hf_fp_edch_e_rnti, tvb, offset, 2, ENC_BIG_ENDIAN);
        offset += 2;
    }

    bit_offset = offset*8;
    /* EDCH subframe header list */
    for (n=0; n < number_of_subframes; n++) {
        guint64    subframe_number;
        guint64    no_of_macis_pdus;
        proto_item *subframe_header_ti;
        proto_tree *subframe_header_tree;

        /* Add subframe header subtree */
        subframe_header_ti = proto_tree_add_string_format(tree, hf_fp_edch_subframe_header, tvb, offset, 0,
                                                          "", "Subframe");
        subframe_header_tree = proto_item_add_subtree(subframe_header_ti, ett_fp_edch_subframe_header);

        /* Spare bit */
        bit_offset++;

        if (!is_common) {
            /* Number of HARQ Retransmissions */
            proto_tree_add_item(subframe_header_tree, hf_fp_edch_harq_retransmissions, tvb,
                                bit_offset/8, 1, ENC_BIG_ENDIAN);
            bit_offset += 4;
        }

        /* Subframe number */
        proto_tree_add_bits_ret_val(subframe_header_tree, hf_fp_edch_subframe_number, tvb,
                                    bit_offset, 3, &subframe_number, ENC_BIG_ENDIAN);
        subframes[n].subframe_number = (guint8)subframe_number;
        bit_offset += 3;

        /* Number of MAC-is PDUs */
        proto_tree_add_bits_ret_val(subframe_header_tree, hf_fp_edch_number_of_mac_is_pdus, tvb,
                                    bit_offset, 4, &no_of_macis_pdus, ENC_BIG_ENDIAN);
        bit_offset += 4;
        subframes[n].number_of_mac_is_pdus = (guint8)no_of_macis_pdus;
        macis_pdus += subframes[n].number_of_mac_is_pdus;

        /* Next 4 bits are spare for T2*/
        if (!is_common) {
            bit_offset += 4;
        }

        /* Show summary in root */
        proto_item_append_text(subframe_header_ti, " (SFN %u, %u MAC-is PDUs)",
                               subframes[n].subframe_number, subframes[n].number_of_mac_is_pdus);
        proto_item_set_len(subframe_header_ti, is_common ? 1 : 2);
    }
    offset = bit_offset / 8;


    /* MAC-is PDU descriptors for each subframe follow */
    for (n=0; n < number_of_subframes; n++) {
        proto_tree *subframe_macis_descriptors_tree;

        /* Add subframe header subtree */
        subframe_macis_descriptors_ti = proto_tree_add_string_format(tree, hf_fp_edch_macis_descriptors, tvb, offset, 0,
                                                                     "", "MAC-is descriptors (SFN %u)", subframes[n].subframe_number);
        proto_item_set_len(subframe_macis_descriptors_ti, subframes[n].number_of_mac_is_pdus*2);
        subframe_macis_descriptors_tree = proto_item_add_subtree(subframe_macis_descriptors_ti,
                                                                 ett_fp_edch_macis_descriptors);

        /* Find a sequence of descriptors for each MAC-is PDU in this subframe */
        for (pdu_no=0; pdu_no < subframes[n].number_of_mac_is_pdus; pdu_no++) {
            proto_item *f_ti = NULL;

            subframes[n].number_of_mac_is_sdus[pdu_no] = 0;

            do {
                /* Check we haven't gone past the limit */
                if (macis_sdus_found++ > total_macis_sdus) {
                    expert_add_info_format(pinfo, f_ti, &ei_fp_mac_is_sdus_miscount, "Found too many (%u) MAC-is SDUs - header said there were %u", macis_sdus_found, (guint16)total_macis_sdus);
                }

                /* LCH-ID */
                subframes[n].mac_is_lchid[pdu_no][subframes[n].number_of_mac_is_sdus[pdu_no]] = (tvb_get_guint8(tvb, offset) & 0xf0) >> 4;
                proto_tree_add_item(subframe_macis_descriptors_tree, hf_fp_edch_macis_lchid, tvb, offset, 1, ENC_BIG_ENDIAN);
                if (subframes[n].mac_is_lchid[pdu_no][subframes[n].number_of_mac_is_sdus[pdu_no]] == 15) {
                    proto_item *ti;

                    /* 4 bits of spare */
                    offset++;

                    /* E-RNTI */
                    ti = proto_tree_add_item(tree, hf_fp_edch_e_rnti, tvb, offset, 2, ENC_BIG_ENDIAN);
                    offset += 2;

                    /* This is only allowed if:
                       - it's the common case AND
                       - it's the first descriptor */
                    if (!is_common) {
                        expert_add_info(pinfo, ti, &ei_fp_e_rnti_t2_edch_frames);
                    }
                    if (subframes[n].number_of_mac_is_sdus[pdu_no] > 0) {
                        expert_add_info(pinfo, ti, &ei_fp_e_rnti_first_entry);
                    }
                    continue;
                }

                /* Length */
                subframes[n].mac_is_length[pdu_no][subframes[n].number_of_mac_is_sdus[pdu_no]] = (tvb_get_ntohs(tvb, offset) & 0x0ffe) >> 1;
                proto_tree_add_item(subframe_macis_descriptors_tree, hf_fp_edch_macis_length, tvb, offset, 2, ENC_BIG_ENDIAN);
                offset++;

                /* Flag */
                F = tvb_get_guint8(tvb, offset) & 0x01;
                f_ti = proto_tree_add_item(subframe_macis_descriptors_tree, hf_fp_edch_macis_flag, tvb, offset, 1, ENC_BIG_ENDIAN);

                subframes[n].number_of_mac_is_sdus[pdu_no]++;

                offset++;
            } while (F == 0);
        }
    }

    /* Check overall count of MAC-is SDUs */
    if (macis_sdus_found != total_macis_sdus) {
        expert_add_info_format(pinfo, subframe_macis_descriptors_ti, &ei_fp_mac_is_sdus_miscount, "Frame contains %u MAC-is SDUs - header said there would be %u!", macis_sdus_found, (guint16)total_macis_sdus);
    }
    header_length = offset;
    /* Now PDUs */
    for (n=0; n < number_of_subframes; n++) {

        /* MAC-is PDU */
        for (pdu_no=0; pdu_no < subframes[n].number_of_mac_is_pdus; pdu_no++) {
            int i;
            guint length = 0;
            umts_mac_is_info * mac_is_info = wmem_new(wmem_file_scope(), umts_mac_is_info);

            mac_is_info->number_of_mac_is_sdus = subframes[n].number_of_mac_is_sdus[pdu_no];
            DISSECTOR_ASSERT(subframes[n].number_of_mac_is_sdus[pdu_no] <= MAX_MAC_FRAMES);
            for (i = 0; i < subframes[n].number_of_mac_is_sdus[pdu_no]; i++) {
                mac_is_info->sdulength[i] = subframes[n].mac_is_length[pdu_no][i];
                mac_is_info->lchid[i] = subframes[n].mac_is_lchid[pdu_no][i];
                length += subframes[n].mac_is_length[pdu_no][i];
            }

            /* Call MAC for this PDU if configured to */
            if (preferences_call_mac_dissectors) {
                p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0, mac_is_info);
                call_dissector_with_data(mac_fdd_edch_type2_handle, tvb_new_subset_remaining(tvb, offset), pinfo, top_level_tree, data);
            }
            else {
                /* Still show data if not decoding as MAC PDU */
                proto_tree_add_item(tree, hf_fp_edch_mac_is_pdu, tvb, offset, length, ENC_NA);
            }

            /* get_mac_tsn_size in packet-umts_mac.h, gets the global_mac_tsn_size preference in umts_mac.c */
            if (get_mac_tsn_size() == MAC_TSN_14BITS) {
                offset += length + 2; /* Plus 2 bytes for TSN 14 bits and SS 2 bit. */
            } else {
                offset += length + 1; /* Plus 1 byte for TSN 6 bits and SS 2 bit. */
            }
        }
    }
    if (preferences_header_checksum) {
        verify_header_crc_edch(tvb, pinfo, header_crc_pi, header_crc, header_length);
    }
    /* Spare extension and payload CRC (optional) */
    dissect_spare_extension_and_crc(tvb, pinfo, tree,
                                    p_fp_info->dch_crc_present, offset, header_length);
}



/**********************************************************/
/* Dissect an HSDSCH channel                              */
/* The data format corresponds to the format              */
/* described in R5 and R6, and frame type 1 in Release 7. */
static void
dissect_hsdsch_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                            int offset, struct fp_info *p_fp_info, void *data)
{
    gboolean is_control_frame;
    guint header_length = 0;
    guint32 header_crc = 0;
    proto_item * header_crc_pi = NULL;

    /* Header CRC */
    header_crc_pi = proto_tree_add_item_ret_uint(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN, &header_crc);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        dissect_common_control(tvb, pinfo, tree, offset, p_fp_info);
        /* For control frame the header CRC is actually frame CRC covering all
         * bytes except the first */
        if (preferences_header_checksum) {
            verify_control_frame_crc(tvb, pinfo, header_crc_pi, (guint16)header_crc);
        }
    }
    else {
        guint8 number_of_pdus;
        guint16 pdu_length;
        guint16 user_buffer_size;
        int i;
        umts_mac_info *macinf;
        rlc_info *rlcinf;
        guint32 user_identity;
        proto_item *item;

        rlcinf = (rlc_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0);
        if (!rlcinf) {
            rlcinf = wmem_new0(wmem_packet_scope(), rlc_info);
        }
        macinf = (umts_mac_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0);
        if (!macinf) {
            macinf = wmem_new0(wmem_packet_scope(), umts_mac_info);
        }

        /**************************************/
        /* HS-DCH data here (type 1 in R7)    */

        /* Frame Seq Nr */
        if ((p_fp_info->release == 6) ||
            (p_fp_info->release == 7)) {

            guint8 frame_seq_no = (tvb_get_guint8(tvb, offset) & 0xf0) >> 4;
            proto_tree_add_item(tree, hf_fp_frame_seq_nr, tvb, offset, 1, ENC_BIG_ENDIAN);

            col_append_fstr(pinfo->cinfo, COL_INFO, "  seqno=%u", frame_seq_no);
        }

        /* CmCH-PI */
        proto_tree_add_item(tree, hf_fp_cmch_pi, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        /* MAC-d PDU Length (13 bits) */
        pdu_length = (tvb_get_ntohs(tvb, offset) >> 3);
        proto_tree_add_item(tree, hf_fp_mac_d_pdu_len, tvb, offset, 2, ENC_BIG_ENDIAN);
        offset += 2;
        macinf->pdu_len = pdu_length;

        if ((p_fp_info->release == 6) ||
            (p_fp_info->release == 7)) {

            /* Flush bit */
            proto_tree_add_item(tree, hf_fp_flush, tvb, offset-1, 1, ENC_BIG_ENDIAN);

            /* FSN/DRT reset bit */
            proto_tree_add_item(tree, hf_fp_fsn_drt_reset, tvb, offset-1, 1, ENC_BIG_ENDIAN);
        }

        /* Num of PDUs */
        number_of_pdus = tvb_get_guint8(tvb, offset);
        item = proto_tree_add_item(tree, hf_fp_num_of_pdu, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;
        if (number_of_pdus > MAX_MAC_FRAMES) {
            expert_add_info_format(pinfo, item, &ei_fp_invalid_frame_count, "Invalid number of PDUs (max is %u)", MAX_MAC_FRAMES);
            return;
        }

        /* User buffer size */
        user_buffer_size = tvb_get_ntohs(tvb, offset);
        proto_tree_add_item(tree, hf_fp_user_buffer_size, tvb, offset, 2, ENC_BIG_ENDIAN);
        offset += 2;

        header_length = offset;


        /* Determine the UE ID to use in RLC */
        user_identity = p_fp_info->com_context_id;
        if(p_fp_info->urnti) {
            user_identity = p_fp_info->urnti;
        }
        /************************/
        /*Configure the pdus*/
        for (i=0;i<number_of_pdus && i<MIN(MAX_MAC_FRAMES, MAX_RLC_CHANS); i++) {
            macinf->content[i] = hsdsch_macdflow_id_mac_content_map[p_fp_info->hsdsch_macflowd_id]; /*MAC_CONTENT_PS_DTCH;*/
            macinf->lchid[i] = fake_lchid_macd_flow[p_fp_info->hsdsch_macflowd_id];/*Faked logical channel id 255 used as a mark if it doesn't exist...*/
            macinf->fake_chid[i] = TRUE;    /**/
            macinf->macdflow_id[i] = p_fp_info->hsdsch_macflowd_id;    /*Save the flow ID (+1 to make it human readable (it's zero indexed!))*/

            /*Check if this is multiplexed (signaled by RRC)*/
            if (p_fp_info->hsdhsch_macfdlow_is_mux[p_fp_info->hsdsch_macflowd_id] ) {
                macinf->ctmux[i] = TRUE;
            } else if (p_fp_info->hsdsch_macflowd_id == 0) {              /*MACd-flow = 0 is often SRB */
                expert_add_info(pinfo, NULL, &ei_fp_maybe_srb);
            } else {
                    macinf->ctmux[i] = FALSE;    /*Either it's multiplexed and not signled or it's not MUX*/
            }

            /* Figure out RLC mode */
            if(p_fp_info->hsdsch_rlc_mode != FP_RLC_MODE_UNKNOWN) {
                /* We know the RLC mode, possibly reported from NBAP */
                rlcinf->mode[i] = (enum rlc_mode)(p_fp_info->hsdsch_rlc_mode - 1);
            }
            else {
                /* Guess the mode by the MACd-flow-ID, basically MACd-flow-ID = 0 then it's SRB0 == UM else AM */
                /* This logic might be incorrect sometimes */
                rlcinf->mode[i] = hsdsch_macdflow_id_rlc_map[p_fp_info->hsdsch_macflowd_id];
            }

            rlcinf->ueid[i] = user_identity;
            rlcinf->li_size[i] = RLC_LI_7BITS;
            rlcinf->deciphered[i] = FALSE;
            rlcinf->ciphered[i] = FALSE;
            rlcinf->rbid[i] = macinf->lchid[i];

#if 0
            /*When a flow has been reconfigured rlc needs to be reset.
             * This needs more work though since we must figure out when the re-configuration becomes
             * active based on the CFN value
             * */
            /*Indicate we need to reset stream*/
            if (p_fp_info->reset_frag) {
                rlc_reset_channel(rlcinf->mode[i], macinf->lchid[i], p_fp_info->is_uplink,  rlcinf->ueid[i] );
                p_fp_info->reset_frag = FALSE;

            }
#endif
        }

        col_append_fstr(pinfo->cinfo, COL_INFO, "  %ux%u-bit PDUs  User-Buffer-Size=%u",
                        number_of_pdus, pdu_length, user_buffer_size);

        /* MAC-d PDUs */
        offset = dissect_macd_pdu_data(tvb, pinfo, tree, offset, pdu_length,
                                       number_of_pdus, p_fp_info, data);

        /* Extra IEs (if there is room for them) */
        if (((p_fp_info->release == 6) ||
             (p_fp_info->release == 7)) &&
            (tvb_reported_length_remaining(tvb, offset) > 2)) {

            int n;
            guint8 flags;
            /* guint8 flag_bytes = 0; */

            /* New IE flags */
            do {
                proto_item *new_ie_flags_ti;
                proto_tree *new_ie_flags_tree;
                guint ies_found = 0;

                /* Add new IE flags subtree */
                new_ie_flags_ti = proto_tree_add_string_format(tree, hf_fp_hsdsch_new_ie_flags, tvb, offset, 1,
                                                              "", "New IE flags");
                new_ie_flags_tree = proto_item_add_subtree(new_ie_flags_ti, ett_fp_hsdsch_new_ie_flags);

                /* Read next byte */
                flags = tvb_get_guint8(tvb, offset);
                /* flag_bytes++; */

                /* Dissect individual bits */
                for (n=0; n < 8; n++) {
                    proto_tree_add_item(new_ie_flags_tree, hf_fp_hsdsch_new_ie_flag[n], tvb, offset, 1, ENC_BIG_ENDIAN);
                    if ((flags >> (7-n)) & 0x01) {
                        ies_found++;
                    }
                }
                offset++;

                proto_item_append_text(new_ie_flags_ti, " (%u IEs found)", ies_found);

                /* Last bit set will indicate another flags byte follows... */
            } while (0); /*((flags & 0x01) && (flag_bytes < 31));*/

            if (1) /*(flags & 0x8) */ {
                /* DRT is shown as mandatory in the diagram (3GPP TS 25.435 V6.3.0),
                   but the description below it states that
                   it should depend upon the first bit.  The detailed description of
                   New IE flags doesn't agree, so treat as mandatory for now... */
                proto_tree_add_item(tree, hf_fp_hsdsch_drt, tvb, offset, 2, ENC_BIG_ENDIAN);
                offset += 2;
            }
        }
        if (preferences_header_checksum) {
            verify_header_crc(tvb, pinfo, header_crc_pi, header_crc, header_length);
        }
        /* Spare Extension and Payload CRC */
        dissect_spare_extension_and_crc(tvb, pinfo, tree, 1, offset, header_length);
    }
}


/******************************************/
/* Dissect an HSDSCH type 2 channel       */
/* (introduced in Release 7)              */
/* N.B. there is currently no support for */
/* frame type 3 (IuR only?)               */
static void
dissect_hsdsch_type_2_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                   int offset, struct fp_info *p_fp_info,
                                   void *data)
{
    gboolean is_control_frame;
    guint32 header_crc = 0;
    proto_item * header_crc_pi = NULL;
    guint16 header_length = 0;

    /* Header CRC */
    header_crc_pi = proto_tree_add_item_ret_uint(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN, &header_crc);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        dissect_common_control(tvb, pinfo, tree, offset, p_fp_info);
        /* For control frame the header CRC is actually frame CRC covering all
         * bytes except the first */
        if (preferences_header_checksum) {
            verify_control_frame_crc(tvb, pinfo, header_crc_pi, (guint16)header_crc);
        }
    }
    else {
        guint8 number_of_pdu_blocks;
        gboolean drt_present = FALSE;
        gboolean fach_present = FALSE;
        guint16 user_buffer_size;
        int n;
        guint j;
        guint64 lchid_val;

        #define MAX_PDU_BLOCKS 31
        guint64 lchid_field[MAX_PDU_BLOCKS];
        guint64 pdu_length[MAX_PDU_BLOCKS];
        guint64 no_of_pdus[MAX_PDU_BLOCKS];

        umts_mac_info *macinf;
        rlc_info *rlcinf;
        guint32 user_identity;

        rlcinf = (rlc_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0);
        if (!rlcinf) {
            rlcinf = wmem_new0(wmem_packet_scope(), rlc_info);
        }
        macinf = (umts_mac_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0);
        if (!macinf) {
            macinf = wmem_new0(wmem_packet_scope(), umts_mac_info);
        }

        /********************************/
        /* HS-DCH type 2 data here      */

        col_append_str(pinfo->cinfo, COL_INFO, "(ehs)");

        /* Frame Seq Nr (4 bits) */
        if ((p_fp_info->release == 6) ||
            (p_fp_info->release == 7)) {

            guint8 frame_seq_no = (tvb_get_guint8(tvb, offset) & 0xf0) >> 4;
            proto_tree_add_item(tree, hf_fp_frame_seq_nr, tvb, offset, 1, ENC_BIG_ENDIAN);

            col_append_fstr(pinfo->cinfo, COL_INFO, "  seqno=%u", frame_seq_no);
        }

        /* CmCH-PI (4 bits) */
        proto_tree_add_item(tree, hf_fp_cmch_pi, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        /* Total number of PDU blocks (5 bits) */
        number_of_pdu_blocks = (tvb_get_guint8(tvb, offset) >> 3);
        proto_tree_add_item(tree, hf_fp_total_pdu_blocks, tvb, offset, 1, ENC_BIG_ENDIAN);

        if (p_fp_info->release == 7) {
            /* Flush bit */
            proto_tree_add_item(tree, hf_fp_flush, tvb, offset, 1, ENC_BIG_ENDIAN);

            /* FSN/DRT reset bit */
            proto_tree_add_item(tree, hf_fp_fsn_drt_reset, tvb, offset, 1, ENC_BIG_ENDIAN);

            /* DRT Indicator */
            drt_present = tvb_get_guint8(tvb, offset) & 0x01;
            proto_tree_add_item(tree, hf_fp_drt_indicator, tvb, offset, 1, ENC_BIG_ENDIAN);
        }
        offset++;

        /* FACH Indicator flag */
        fach_present = (tvb_get_guint8(tvb, offset) & 0x80) >> 7;
        proto_tree_add_item(tree, hf_fp_fach_indicator, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        /* User buffer size */
        user_buffer_size = tvb_get_ntohs(tvb, offset);
        proto_tree_add_item(tree, hf_fp_user_buffer_size, tvb, offset, 2, ENC_BIG_ENDIAN);
        offset += 2;

        col_append_fstr(pinfo->cinfo, COL_INFO, "  User-Buffer-Size=%u", user_buffer_size);



        /********************************************************************/
        /* Now read number_of_pdu_blocks header entries                     */
        for (n=0; n < number_of_pdu_blocks; n++) {
            proto_item *pdu_block_header_ti;
            proto_tree *pdu_block_header_tree;
            int        block_header_start_offset = offset;

            /* Add PDU block header subtree */
            pdu_block_header_ti = proto_tree_add_string_format(tree, hf_fp_hsdsch_pdu_block_header,
                                                               tvb, offset, 0,
                                                               "",
                                                               "PDU Block Header");
            pdu_block_header_tree = proto_item_add_subtree(pdu_block_header_ti,
                                                           ett_fp_hsdsch_pdu_block_header);

            /* MAC-d/c PDU length in this block (11 bits) */
            proto_tree_add_bits_ret_val(pdu_block_header_tree, hf_fp_pdu_length_in_block, tvb,
                                        (offset*8) + ((n % 2) ? 4 : 0), 11,
                                        &pdu_length[n], ENC_BIG_ENDIAN);
            if ((n % 2) == 0)
                offset++;
            else
                offset += 2;


            /* # PDUs in this block (4 bits) */
            proto_tree_add_bits_ret_val(pdu_block_header_tree, hf_fp_pdus_in_block, tvb,
                                        (offset*8) + ((n % 2) ? 0 : 4), 4,
                                        &no_of_pdus[n], ENC_BIG_ENDIAN);
            if ((n % 2) == 0) {
                offset++;
            }

            /* Logical channel ID in block (4 bits) */
            proto_tree_add_bits_ret_val(pdu_block_header_tree, hf_fp_lchid, tvb,
                                        (offset*8) + ((n % 2) ? 4 : 0), 4,
                                        &lchid_field[n], ENC_BIG_ENDIAN);
            if ((n % 2) == 1) {
                offset++;
            }
            else {
                if (n == (number_of_pdu_blocks-1)) {
                    /* Byte is padded out for last block */
                    offset++;
                }
            }

            /* Append summary to header tree root */
            proto_item_append_text(pdu_block_header_ti,
                                   " (lch:%u, %u pdus of %u bytes)",
                                   (guint16)lchid_field[n],
                                   (guint16)no_of_pdus[n],
                                   (guint16)pdu_length[n]);

            /* Set length of header tree item */
            if (((n % 2) == 0) && (n < (number_of_pdu_blocks-1))) {
                proto_item_set_len(pdu_block_header_ti,
                                   offset - block_header_start_offset+1);
            }
            else {
                proto_item_set_len(pdu_block_header_ti,
                                   offset - block_header_start_offset);
            }
        }

        header_length = offset;

        /**********************************************/
        /* Optional fields indicated by earlier flags */
        if (drt_present) {
            /* DRT */
            proto_tree_add_item(tree, hf_fp_drt, tvb, offset, 2, ENC_BIG_ENDIAN);
            offset += 2;
        }

        if (fach_present) {
            /* H-RNTI: */
            proto_tree_add_item(tree, hf_fp_hrnti, tvb, offset, 2, ENC_BIG_ENDIAN);
            offset += 2;

            /* RACH Measurement Result */
            proto_tree_add_item(tree, hf_fp_rach_measurement_result, tvb, offset, 2, ENC_BIG_ENDIAN);
            offset++;
        }


        /* Determine the UE ID to use in RLC */
        user_identity = p_fp_info->com_context_id;
        if(p_fp_info->urnti) {
            user_identity = p_fp_info->urnti;
        }
        /********************************************************************/
        /* Now read the MAC-d/c PDUs for each block using info from headers */
        for (n=0; n < number_of_pdu_blocks; n++) {
            for (j=0;j<no_of_pdus[n];j++) {

                /*Configure (signal to lower layers) the PDU!*/
                if (lchid_field[n] != 0x0f) {
                    lchid_val = lchid_field[n] + 1; /* Add 1 since 'LCHID' field is zero indexed. ie field value = 0 => Actual L-CHID = 1*/
                    macinf->content[j] = lchId_type_table[lchid_val];
                    macinf->lchid[j] = (guint8)lchid_val;
                    macinf->macdflow_id[j] = p_fp_info->hsdsch_macflowd_id;
                    /*Figure out RLC_MODE based on MACd-flow-ID, basically MACd-flow-ID = 0 then it's SRB0 == UM else AM*/
                    rlcinf->mode[j] = lchId_rlc_map[lchid_val];

                    macinf->ctmux[n] = FALSE;

                    rlcinf->li_size[j] = RLC_LI_7BITS;
                    rlcinf->ciphered[j] = FALSE;
                    rlcinf->deciphered[j] = FALSE;
                    rlcinf->rbid[j] = (guint8)lchid_val;

                    rlcinf->ueid[j] = user_identity;
                }
                else {
                    /* LCHID field is 15. This value indicates BCCH or PCCH mapped on HS-DSCH*/
                    /* The dissector does not handle this case yet, so we are filling zeroes and default values below*/
                    macinf->content[j] = MAC_CONTENT_UNKNOWN;
                    macinf->lchid[j] = 0; /* LCHID field doesn't reflect a real ID in this case*/
                    macinf->macdflow_id[j] = 0;
                    macinf->ctmux[j] = FALSE;

                    rlcinf->mode[j] = RLC_TM; /* PCCH and BCCH should be using RLC TM? */
                    rlcinf->li_size[j] = RLC_LI_7BITS;
                    rlcinf->ciphered[j] = FALSE;
                    rlcinf->deciphered[j] = FALSE;
                    rlcinf->rbid[j] = 0;
                    rlcinf->ueid[j] = 0;
                }
            }

            /* Add PDU block header subtree */
            offset = dissect_macd_pdu_data_type_2(tvb, pinfo, tree, offset,
                                                  (guint16)pdu_length[n],
                                                  (guint16)no_of_pdus[n],
                                                  p_fp_info, data);
        }
        if (preferences_header_checksum) {
            verify_header_crc(tvb, pinfo, header_crc_pi, header_crc, header_length);
        }
        /* Spare Extension and Payload CRC */
        dissect_spare_extension_and_crc(tvb, pinfo, tree, 1, offset, header_length);
    }
}
/**
* Dissect and CONFIGURE hsdsch_common channel.
*
* This will dissect hsdsch common channels of type 2, so this is
* very similar to regular type two (ehs) the difference being how
* the configuration is done. NOTE: VERY EXPERIMENTAL.
*
* @param tvb the tv buffer of the current data
* @param pinfo the packet info of the current data
* @param tree the tree to append this item to
* @param offset the offset in the tvb
* @param p_fp_info FP-packet information
*/
static
void dissect_hsdsch_common_channel_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                        int offset, struct fp_info *p_fp_info,
                                        void *data)
{
    gboolean is_control_frame;
    guint32 header_crc = 0;
    proto_item * header_crc_pi = NULL;
    guint header_length = 0;

    /* Header CRC */
    header_crc_pi = proto_tree_add_item_ret_uint(tree, hf_fp_header_crc, tvb, offset, 1, ENC_BIG_ENDIAN, &header_crc);

    /* Frame Type */
    proto_tree_add_item_ret_uint(tree, hf_fp_ft, tvb, offset, 1, ENC_BIG_ENDIAN, &is_control_frame);
    offset++;

    col_append_str(pinfo->cinfo, COL_INFO, is_control_frame ? " [Control] " : " [Data] ");

    if (is_control_frame) {
        dissect_common_control(tvb, pinfo, tree, offset, p_fp_info);
        /* For control frame the header CRC is actually frame CRC covering all
         * bytes except the first */
        if (preferences_header_checksum) {
            verify_control_frame_crc(tvb, pinfo, header_crc_pi, (guint16)header_crc);
        }
    }
    else {
        guint8 number_of_pdu_blocks;
        gboolean drt_present = FALSE;
        gboolean fach_present = FALSE;
        guint16 user_buffer_size;
        int n;
        guint j;

        #define MAX_PDU_BLOCKS 31
        guint64 lchid[MAX_PDU_BLOCKS];
        guint64 pdu_length[MAX_PDU_BLOCKS];
        guint64 no_of_pdus[MAX_PDU_BLOCKS];
        guint8 newieflags = 0;

        umts_mac_info *macinf;
        rlc_info *rlcinf;

        rlcinf = (rlc_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0);
        if (!rlcinf) {
            rlcinf = wmem_new0(wmem_packet_scope(), rlc_info);
        }
        macinf = (umts_mac_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0);
        if (!macinf) {
            macinf = wmem_new0(wmem_packet_scope(), umts_mac_info);
        }
        /********************************/
        /* HS-DCH type 2 data here      */

        col_append_str(pinfo->cinfo, COL_INFO, "(ehs)");

        /* Frame Seq Nr (4 bits) */
        if ((p_fp_info->release == 6) ||
            (p_fp_info->release == 7)) {

            guint8 frame_seq_no = (tvb_get_guint8(tvb, offset) & 0xf0) >> 4;
            proto_tree_add_item(tree, hf_fp_frame_seq_nr, tvb, offset, 1, ENC_BIG_ENDIAN);

            col_append_fstr(pinfo->cinfo, COL_INFO, "  seqno=%u", frame_seq_no);
        }

        /* CmCH-PI (4 bits) */
        proto_tree_add_item(tree, hf_fp_cmch_pi, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        /* Total number of PDU blocks (5 bits) */
        number_of_pdu_blocks = (tvb_get_guint8(tvb, offset) >> 3);
        proto_tree_add_item(tree, hf_fp_total_pdu_blocks, tvb, offset, 1, ENC_BIG_ENDIAN);

        if (p_fp_info->release == 7) {
            /* Flush bit */
            proto_tree_add_item(tree, hf_fp_flush, tvb, offset, 1, ENC_BIG_ENDIAN);

            /* FSN/DRT reset bit */
            proto_tree_add_item(tree, hf_fp_fsn_drt_reset, tvb, offset, 1, ENC_BIG_ENDIAN);

            /* DRT Indicator */
            drt_present = tvb_get_guint8(tvb, offset) & 0x01;
            proto_tree_add_item(tree, hf_fp_drt_indicator, tvb, offset, 1, ENC_BIG_ENDIAN);
        }
        offset++;

        /* FACH Indicator flag */
        fach_present = (tvb_get_guint8(tvb, offset) & 0x80) >> 7;
        proto_tree_add_item(tree, hf_fp_fach_indicator, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset++;

        /* User buffer size */
        user_buffer_size = tvb_get_ntohs(tvb, offset);
        proto_tree_add_item(tree, hf_fp_user_buffer_size, tvb, offset, 2, ENC_BIG_ENDIAN);
        offset += 2;

        col_append_fstr(pinfo->cinfo, COL_INFO, "  User-Buffer-Size=%u", user_buffer_size);


        /********************************************************************/
        /* Now read number_of_pdu_blocks header entries                     */
        for (n=0; n < number_of_pdu_blocks; n++) {
            proto_item *pdu_block_header_ti;
            proto_item *item;
            proto_tree *pdu_block_header_tree;
            int        block_header_start_offset = offset;

            /* Add PDU block header subtree */
            pdu_block_header_ti = proto_tree_add_string_format(tree, hf_fp_hsdsch_pdu_block_header,
                                                               tvb, offset, 0,
                                                               "",
                                                               "PDU Block Header");
            pdu_block_header_tree = proto_item_add_subtree(pdu_block_header_ti,
                                                           ett_fp_hsdsch_pdu_block_header);

            /* MAC-d/c PDU length in this block (11 bits) */
            proto_tree_add_bits_ret_val(pdu_block_header_tree, hf_fp_pdu_length_in_block, tvb,
                                        (offset*8) + ((n % 2) ? 4 : 0), 11,
                                        &pdu_length[n], ENC_BIG_ENDIAN);
            if ((n % 2) == 0)
                offset++;
            else
                offset += 2;


            /* # PDUs in this block (4 bits) */
            item = proto_tree_add_bits_ret_val(pdu_block_header_tree, hf_fp_pdus_in_block, tvb,
                                        (offset*8) + ((n % 2) ? 0 : 4), 4,
                                        &no_of_pdus[n], ENC_BIG_ENDIAN);
            if ((n % 2) == 0) {
                offset++;
            }
            if (no_of_pdus[n] > MAX_MAC_FRAMES) {
                expert_add_info_format(pinfo, item, &ei_fp_invalid_frame_count, "Invalid number of PDUs (max is %u)", MAX_MAC_FRAMES);
                return;
            }

            /* Logical channel ID in block (4 bits) */
            proto_tree_add_bits_ret_val(pdu_block_header_tree, hf_fp_lchid, tvb,
                                        (offset*8) + ((n % 2) ? 4 : 0), 4,
                                        &lchid[n], ENC_BIG_ENDIAN);
            if ((n % 2) == 1) {
                offset++;
            }
            else {
                if (n == (number_of_pdu_blocks-1)) {
                    /* Byte is padded out for last block */
                    offset++;
                }
            }

            /* Append summary to header tree root */
            proto_item_append_text(pdu_block_header_ti,
                                   " (lch:%u, %u pdus of %u bytes)",
                                   (guint16)lchid[n],
                                   (guint16)no_of_pdus[n],
                                   (guint16)pdu_length[n]);

            /* Set length of header tree item */
            if (((n % 2) == 0) && (n < (number_of_pdu_blocks-1))) {
                proto_item_set_len(pdu_block_header_ti,
                                   offset - block_header_start_offset+1);
            }
            else {
                proto_item_set_len(pdu_block_header_ti,
                                   offset - block_header_start_offset);
            }
        }

        header_length = offset;

        /**********************************************/
        /* Optional fields indicated by earlier flags */
        if (drt_present) {
            /* DRT */
            proto_tree_add_item(tree, hf_fp_drt, tvb, offset, 2, ENC_BIG_ENDIAN);
            offset += 2;
        }

        if (fach_present) {
            /* H-RNTI: */
            proto_tree_add_item(tree, hf_fp_hrnti, tvb, offset, 2, ENC_BIG_ENDIAN);
            offset += 2;

            /* RACH Measurement Result */
            proto_tree_add_item(tree, hf_fp_rach_measurement_result, tvb, offset, 1, ENC_BIG_ENDIAN);
            offset++;
        }

        /********************************************************************/
        /* Now read the MAC-d/c PDUs for each block using info from headers */
        for (n=0; n < number_of_pdu_blocks; n++) {
            tvbuff_t *next_tvb;
            for (j=0; j<no_of_pdus[n]; j++) {
                /* If all bits are set, then this is BCCH or PCCH according to: 25.435 paragraph: 6.2.7.31 */
                if (lchid[n] == 0xF) {
                    /* In the very few test cases I've seen, this seems to be
                     * BCCH with transparent MAC layer. Therefore skip right to
                     * rlc_bcch and hope for the best. */
                    next_tvb = tvb_new_subset_length(tvb, offset, (gint)pdu_length[n]);
                    call_dissector_with_data(rlc_bcch_handle, next_tvb, pinfo, top_level_tree, data);
                    offset += (gint)pdu_length[n];
                } else { /* Else go for CCCH UM, this seems to work. */
                    p_fp_info->hsdsch_entity = ehs; /* HSDSCH type 2 */
                    /* TODO: use cur_tb or subnum everywhere. */
                    if (j >= MAX_MAC_FRAMES) {
                        /* Should not happen as we check no_of_pdus[n]*/
                        expert_add_info_format(pinfo, tree, &ei_fp_invalid_frame_count, "Invalid frame count (max is %u)", MAX_MAC_FRAMES);
                        return;
                    }
                    p_fp_info->cur_tb = j; /* set cur_tb for MAC */
                    pinfo->fd->subnum = j; /* set subframe number for RRC */
                    macinf->content[j] = MAC_CONTENT_CCCH;
                    macinf->lchid[j] = (guint8)lchid[n]+1; /*Add 1 since it is zero indexed? */
                    macinf->macdflow_id[j] = p_fp_info->hsdsch_macflowd_id;
                    macinf->ctmux[j] = FALSE;

                    rlcinf->li_size[j] = RLC_LI_7BITS;
                    rlcinf->ciphered[j] = FALSE;
                    rlcinf->deciphered[j] = FALSE;
                    rlcinf->rbid[j] = (guint8)lchid[n]+1;
                    rlcinf->ueid[j] = p_fp_info->channel; /*We need to fake "UE ID"*/

                    next_tvb = tvb_new_subset_length(tvb, offset, (gint)pdu_length[n]);
                    call_dissector_with_data(mac_fdd_hsdsch_handle, next_tvb, pinfo, top_level_tree, data);

                    offset += (gint)pdu_length[n];
                }
            }
        }

        /* New IE Flags */
        newieflags = tvb_get_guint8(tvb, offset);
        /* If newieflags == 0000 0010 then this indicates that there is a
         * HS-DSCH physical layer category and no other New IE flags. */
        if (newieflags == 2) {
            /* HS-DSCH physical layer category presence bit. */
            proto_tree_add_uint(tree, hf_fp_hsdsch_new_ie_flag[6], tvb, offset, 1, newieflags);
            offset++;
            /* HS-DSCH physical layer category. */
            proto_tree_add_bits_item(tree, hf_fp_hsdsch_physical_layer_category, tvb, offset*8, 6, ENC_BIG_ENDIAN);
            offset++;
        }
        if (preferences_header_checksum) {
            verify_header_crc(tvb, pinfo, header_crc_pi, header_crc, header_length);
        }
        /* Spare Extension and Payload CRC */
        dissect_spare_extension_and_crc(tvb, pinfo, tree, 1, offset, header_length);
    }
}
/* Validates the header CRC in a Control FP frame */
/* Should only be used in heuristic dissectors! */
static gboolean
check_control_frame_crc_for_heur(tvbuff_t * tvb)
{
    guint8 crc = 0;
    guint8 calc_crc = 0;
    guint8 * data = NULL;
    guint  reported_length = tvb_reported_length(tvb);

    if (reported_length == 0 || reported_length > tvb_captured_length(tvb))
        return FALSE;

    crc = tvb_get_guint8(tvb, 0) >> 1;
    /* Get data. */
    data = (guint8 *)tvb_memdup(wmem_packet_scope(), tvb, 0, tvb_reported_length(tvb));
    /* Include only FT flag bit in CRC calculation. */
    data[0] = data[0] & 1;
    calc_crc = crc7update(0, data, tvb_reported_length(tvb));
    calc_crc = crc7finalize(calc_crc);

    return calc_crc == crc;
}
/* Validates the header CRC in a Data FP frame */
/* Should only be used in heuristic dissectors! */
static gboolean
check_header_crc_for_heur(tvbuff_t *tvb, guint16 header_length)
{
    guint8 crc = 0;
    guint8 calc_crc = 0;
    const guint8 * data = NULL;

    if (header_length > tvb_captured_length(tvb))
        return FALSE;

    crc = tvb_get_guint8(tvb, 0) >> 1;
    /* Get data of header excluding the first byte */
    data = tvb_get_ptr(tvb, 1, header_length - 1);

    calc_crc = crc7update(0, data, header_length - 1);
    calc_crc = crc7finalize(calc_crc);

    return calc_crc == crc;
}
/* Validates the payload CRC in a Data FP frame */
/* Should only be used in heuristic dissectors! */
static gboolean
check_payload_crc_for_heur(tvbuff_t *tvb, guint16 header_length)
{
    guint16 reported_length;
    guint16 crc_index;
    guint16 crc = 0;
    guint16 calc_crc = 0;
    guint16 payload_index;
    guint16 payload_length;
    const guint8 *data = NULL;

    reported_length = tvb_reported_length(tvb);
    if (reported_length < 2 || reported_length > tvb_captured_length(tvb)) {
        return FALSE;
    }
    /* Payload CRC is in the last 2 bytes of the packet */
    crc_index = reported_length - 2;
    crc = tvb_get_bits16(tvb, crc_index * 8, 16, ENC_BIG_ENDIAN);

    payload_index = header_length; /* payload first index is the same as the header length */
    payload_length = (reported_length - payload_index) - 2;
    data = tvb_get_ptr(tvb, payload_index, payload_length);
    calc_crc = crc16_8005_noreflect_noxor(data, payload_length);

    return calc_crc == crc;
}
/* Validates the header CRC in a E-DCH Data FP frame */
/* Should only be used in heuristic dissectors! */
static gboolean
check_edch_header_crc_for_heur(tvbuff_t *tvb, guint16 header_length)
{
    guint16 crc = 0;
    guint16 calc_crc = 0;
    guint8 * data = NULL;

    if (header_length > tvb_captured_length(tvb))
        return FALSE;

    crc = (tvb_get_bits8(tvb, 0, 7) << 4) + tvb_get_bits8(tvb, 8, 4);
    /* Get data of header excluding the first byte */
    data = (guint8 *)tvb_memdup(wmem_packet_scope(), tvb, 1, header_length-1);
    /*Zero the part in the second byte which contains part of the CRC*/
    data[0] = data[0] & 0x0f;

    calc_crc = crc11_307_noreflect_noxor(data, header_length-1);

    return calc_crc == crc;
}
/* Generates a unique 32bit identifier based on the frame's metadata */
/* This ID is used in the RLC dissector for reassembly */
/* Should only be used in heuristic dissectors! */
static guint32
generate_ue_id_for_heur(packet_info *pinfo)
{
    if (pinfo->ptype == PT_UDP &&  pinfo->src.type == AT_IPv4 &&  pinfo->dst.type == AT_IPv4) {
        /* This logic assumes FP is delivered over IP/UDP*/
        /* Will return the same ID even if the address and ports are reversed */

        /* srcXor: [ ------- Source Address ------- ] (4 bytes)*/
        /*                         XOR                         */
        /*         [  Source Port  ][  Source Port  ] (4 bytes)*/
        int srcXor = pntoh32(pinfo->src.data) ^ ((pinfo->srcport << 16) | (pinfo->srcport));

        /* dstXor: [ ---- Destination  Address ---- ] (4 bytes)*/
        /*                         XOR                         */
        /*         [ - Dest Port - ][ - Dest Port - ] (4 bytes)*/
        int dstXor = pntoh32(pinfo->dst.data) ^ ((pinfo->destport << 16) | (pinfo->destport));
        return srcXor ^ dstXor;
    }
    else {
        /* Fallback - When IP and/or UDP are missing for whatever reason */
        /* Using the frame number of the first heuristicly dissected frame as the UE ID should be unique enough */
        /* The bitwise NOT operator is used to prevent low UE ID values which are likely to collide */
        /* with legitimate UE IDs derived from C-RNTIs in FACH/RACH */
        return ~(pinfo->num);
    }
}
/* Fills common PCH information in a 'fp conversation info' object */
/* Should only be used in heuristic dissectors! */
static void
fill_pch_conversation_info_for_heur(umts_fp_conversation_info_t* umts_fp_conversation_info ,packet_info *pinfo)
{
    umts_fp_conversation_info->iface_type = IuB_Interface;
    umts_fp_conversation_info->division = Division_FDD;
    umts_fp_conversation_info->dl_frame_number = pinfo->num;
    umts_fp_conversation_info->ul_frame_number = pinfo->num;
    umts_fp_conversation_info->dch_crc_present = 1;
    umts_fp_conversation_info->com_context_id = generate_ue_id_for_heur(pinfo);
    umts_fp_conversation_info->rlc_mode = FP_RLC_AM;
    copy_address_wmem(wmem_file_scope(), &(umts_fp_conversation_info->crnc_address), &pinfo->src);
    umts_fp_conversation_info->crnc_port = pinfo->srcport;
    umts_fp_conversation_info->channel = CHANNEL_PCH;
    umts_fp_conversation_info->num_dch_in_flow = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].num_dl_chans = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_num_tbs[1] = 1;
    umts_fp_conversation_info->channel_specific_info = (void*)wmem_new0(wmem_file_scope(), fp_pch_channel_info_t);
}
/* Attaches conversation info to both the downlink and uplink 'conversations' (streams) */
/* (Required since only one of them is checked in every dissected FP packet) */
/* Should only be used in heuristic dissectors! */
static void
set_both_sides_umts_fp_conv_data(packet_info *pinfo, umts_fp_conversation_info_t *umts_fp_conversation_info)
{
    conversation_t   *packet_direction_conv;
    conversation_t   *other_direction_conv;

    if (pinfo == NULL) {
        return;
    }

    /* Finding or creating conversation for the way the packet is heading */
    packet_direction_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
        conversation_pt_to_endpoint_type(pinfo->ptype),
        pinfo->destport, pinfo->srcport, NO_ADDR_B);

    if (packet_direction_conv == NULL) {
        /* Conversation does not exist yet, creating one now. */
        packet_direction_conv = conversation_new(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
            conversation_pt_to_endpoint_type(pinfo->ptype),
            pinfo->destport, pinfo->srcport, NO_ADDR_B);
    }
    conversation_add_proto_data(packet_direction_conv, proto_fp, umts_fp_conversation_info);

    /* Finding or creating conversation for the other side */
    other_direction_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_src, &pinfo->net_dst,
        conversation_pt_to_endpoint_type(pinfo->ptype),
        pinfo->srcport, pinfo->destport, NO_ADDR_B);

    if (other_direction_conv == NULL) {
        /* Conversation does not exist yet, creating one now. */
        other_direction_conv = conversation_new(pinfo->num, &pinfo->net_src, &pinfo->net_dst,
            conversation_pt_to_endpoint_type(pinfo->ptype),
            pinfo->srcport, pinfo->destport, NO_ADDR_B);
    }
    conversation_add_proto_data(other_direction_conv, proto_fp, umts_fp_conversation_info);

}
static gboolean
heur_dissect_fp_dcch_over_dch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    conversation_t   *p_conv;
    umts_fp_conversation_info_t* umts_fp_conversation_info = NULL;
    struct fp_info *p_fp_info;
    guint32 captured_length;
    guint32 reported_length;
    guint8 frame_type;
    guint8 tfi;
    guint8 pch_collisions_byte;

    /* Trying to find existing conversation */
    p_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
        conversation_pt_to_endpoint_type(pinfo->ptype),
        pinfo->destport, pinfo->srcport, NO_ADDR_B);

    if (p_conv != NULL) {
        /* Checking if the conversation was already framed */
        umts_fp_conversation_info = (umts_fp_conversation_info_t *)conversation_get_proto_data(p_conv, proto_fp);
        if (umts_fp_conversation_info) {
            if (umts_fp_conversation_info->channel == CHANNEL_DCH) {
                conversation_set_dissector(p_conv, fp_handle);
                dissect_fp(tvb, pinfo, tree, data);
                return TRUE;
            }
            else if (umts_fp_conversation_info->channel != CHANNEL_UNKNOWN){
                /* This conversation was successfuly framed as ANOTHER type */
                return FALSE;
            }
        }
    }

    /* Making sure FP info isn't already attached */
    p_fp_info = (fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);
    if (p_fp_info) {
        return FALSE;
    }

    frame_type = tvb_get_guint8(tvb, 0) & 0x01;
    if (frame_type == 1) { /* is 'control' frame type*/
        return FALSE;
    }

    /* Making sure we have at least enough bytes for header (3) + footer (2) */
    captured_length = tvb_captured_length(tvb);
    reported_length = tvb_reported_length(tvb);
    if (captured_length < 5) {
        return FALSE;
    }
    tfi = tvb_get_guint8(tvb, 2) & 0x1f;

    /* Checking if this is a DCH frame with 0 TBs*/
    if (tfi == 0x00)
    {
        if (reported_length != 5 /* DL */ && reported_length != 7 /* UL */) {
            return FALSE;
        }
        if (!check_header_crc_for_heur(tvb, 3)) {
            return FALSE;
        }
        if (!check_payload_crc_for_heur(tvb, 3)) {
            return FALSE;
        }
        /* All checks passed - This is an unknown DCH FP frame. */
        /* To allow dissection of this frame after umts_fp_conversation_info will be added in a later frame */
        /* the conversation must be created here if it doesn't exist yet*/
        if (p_conv == NULL) {
            conversation_new(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
                conversation_pt_to_endpoint_type(pinfo->ptype),
                pinfo->destport, pinfo->srcport, NO_ADDR_B);
        }
        return FALSE;
    }

    /* Checking this is a DCH frame with 1 TB */
    if (tfi != 0x01) {
        return FALSE;
    }

    /* Expecting specific lengths: 24 for downlink frames, 26 for uplink frames */
    /* This is the common Transport Format of DCCH over DCH ( See 3GPP TR 25.944 / 4.1.1.3.1.1 ) */
    if (reported_length != 24 /* DL */ && reported_length != 26 /* UL */) {
        return FALSE;
    }

    if (!check_header_crc_for_heur(tvb, 3)) {
        return FALSE;
    }
    if (!check_payload_crc_for_heur(tvb, 3)) {
        return FALSE;
    }

    /* Checking if the 4th byte in the frame is zeroed. In this case the CRC checks aren't */
    /* deterministic enough to gurantee this is a DCH since this packet could also be a PCH frame */
    /* with PI Bitmap of 18 bytes + 0 TBs (Both CRCs will match for both formats) */
    pch_collisions_byte = tvb_get_guint8(tvb, 3);
    if (pch_collisions_byte == 0) {
        return FALSE;
    }

    if(!umts_fp_conversation_info) {
        umts_fp_conversation_info = wmem_new0(wmem_file_scope(), umts_fp_conversation_info_t);
        set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
    }
    umts_fp_conversation_info->iface_type = IuB_Interface;
    umts_fp_conversation_info->division = Division_FDD;
    umts_fp_conversation_info->dl_frame_number = pinfo->num;
    umts_fp_conversation_info->ul_frame_number = pinfo->num;
    umts_fp_conversation_info->dch_crc_present = 1;
    umts_fp_conversation_info->com_context_id = generate_ue_id_for_heur(pinfo);
    umts_fp_conversation_info->rlc_mode = FP_RLC_AM;
    if (reported_length == 24) { /* Downlink */
        copy_address_wmem(wmem_file_scope(), &(umts_fp_conversation_info->crnc_address), &pinfo->src);
        umts_fp_conversation_info->crnc_port = pinfo->srcport;
    }
    else { /* Uplink*/
        copy_address_wmem(wmem_file_scope(), &(umts_fp_conversation_info->crnc_address), &pinfo->dst);
        umts_fp_conversation_info->crnc_port = pinfo->destport;
    }
    umts_fp_conversation_info->channel = CHANNEL_DCH;
    umts_fp_conversation_info->num_dch_in_flow = 1;
    umts_fp_conversation_info->dch_ids_in_flow_list[0] = 31;
    umts_fp_conversation_info->fp_dch_channel_info[0].num_dl_chans = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_num_tbs[1] = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_tf_size[1] = 148;
    umts_fp_conversation_info->fp_dch_channel_info[0].num_ul_chans = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].ul_chan_num_tbs[1] = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].ul_chan_tf_size[1] = 148;
    conversation_set_dissector(find_or_create_conversation(pinfo), fp_handle);
    dissect_fp(tvb, pinfo, tree, data);
    return TRUE;
}
static gboolean
heur_dissect_fp_fach1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    conversation_t   *p_conv;
    fp_fach_channel_info_t* fp_fach_channel_info;
    umts_fp_conversation_info_t* umts_fp_conversation_info = NULL;
    struct fp_info *p_fp_info;
    guint32 captured_length;
    guint32 reported_length;
    guint8 frame_type;
    guint8 tfi;
    guint8 tctf;

    /* Finding or creating conversation */
    p_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
        conversation_pt_to_endpoint_type(pinfo->ptype),
        pinfo->destport, pinfo->srcport, NO_ADDR_B);

    if (p_conv != NULL) {
        /* Checking if the conversation was already framed */
        umts_fp_conversation_info = (umts_fp_conversation_info_t *)conversation_get_proto_data(p_conv, proto_fp);
        if (umts_fp_conversation_info) {
            if (umts_fp_conversation_info->channel == CHANNEL_FACH_FDD) {
                conversation_set_dissector(p_conv, fp_handle);
                dissect_fp(tvb, pinfo, tree, data);
                return TRUE;
            }
            else if (umts_fp_conversation_info->channel != CHANNEL_UNKNOWN){
                /* This conversation was successfuly framed as ANOTHER type */
                return FALSE;
            }
        }
    }
    /* Making sure we have at least enough bytes for header (4) + footer (2) */
    captured_length = tvb_captured_length(tvb);
    if(captured_length < 6) {
        return FALSE;
    }

    /* Expecting specific lengths: 51 for frames with 1 TB */
    /* This is a common Transport Format of FACH ( See 3GPP TR 25.944 / 4.1.1.2 'FACH1' ) */
    reported_length = tvb_reported_length(tvb);
    if (reported_length != 51) {
        return FALSE;
    }

    p_fp_info = (fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);

    /* Making sure FP info isn't already attached */
    if (p_fp_info) {
        return FALSE;
    }

    frame_type = tvb_get_guint8(tvb, 0) & 0x01;
    if (frame_type == 1) { /* is 'control' frame type*/
                           /* We can't tell the FP type and content of control frames */
        return FALSE;
    }

    tfi = tvb_get_guint8(tvb, 2) & 0x1f;
    if (tfi != 0x01) {
        return FALSE;
    }

    tctf = tvb_get_guint8(tvb, 4);
    /* Asserting the TCTF field contains a valid (non reserved) value according to TS 25.321 Table 9.2.1-2 */
    if (tctf != 0x40 && /* CCCH */
        tctf != 0x50 && /* MCCH */
        tctf != 0x5F && /* MSCH */
        tctf != 0x80 && /* CTCH */
        (tctf >> 4) != 0x06 && /* MTCH */
        (tctf >> 6) != 0x00 && /* BCCH */
        (tctf >> 6) != 0x03) { /* DCCH or DTCH over FACH */
        return FALSE;
    }

    if (!check_header_crc_for_heur(tvb, 4)) {
        return FALSE;
    }
    if (!check_payload_crc_for_heur(tvb, 4)) {
        return FALSE;
    }

    if(!umts_fp_conversation_info) {
        umts_fp_conversation_info = wmem_new0(wmem_file_scope(), umts_fp_conversation_info_t);
        set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
    }
    umts_fp_conversation_info->iface_type = IuB_Interface;
    umts_fp_conversation_info->division = Division_FDD;
    umts_fp_conversation_info->dl_frame_number = pinfo->num;
    umts_fp_conversation_info->ul_frame_number = pinfo->num;
    umts_fp_conversation_info->dch_crc_present = 1;
    umts_fp_conversation_info->com_context_id = generate_ue_id_for_heur(pinfo);
    umts_fp_conversation_info->rlc_mode = FP_RLC_AM;
    copy_address_wmem(wmem_file_scope(), &(umts_fp_conversation_info->crnc_address), &pinfo->src);
    umts_fp_conversation_info->crnc_port = pinfo->srcport;
    umts_fp_conversation_info->channel = CHANNEL_FACH_FDD;
    umts_fp_conversation_info->num_dch_in_flow = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].num_dl_chans = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_num_tbs[1] = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_tf_size[1] = 360;
    /* Adding the 'channel specific info' for FACH */
    fp_fach_channel_info = wmem_new0(wmem_file_scope(), fp_fach_channel_info_t);
    fp_fach_channel_info->crnti_to_urnti_map = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
    umts_fp_conversation_info->channel_specific_info = (void*)fp_fach_channel_info;

    conversation_set_dissector(find_or_create_conversation(pinfo), fp_handle);
    dissect_fp(tvb, pinfo, tree, data);
    return TRUE;
}
static gboolean
heur_dissect_fp_fach2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    conversation_t   *p_conv;
    fp_fach_channel_info_t* fp_fach_channel_info;
    umts_fp_conversation_info_t* umts_fp_conversation_info = NULL;
    struct fp_info *p_fp_info;
    guint32 captured_length;
    guint32 reported_length;
    guint8 frame_type;
    guint8 tfi;
    guint8 tctf;

    /* Finding or creating conversation */
    p_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
        conversation_pt_to_endpoint_type(pinfo->ptype),
        pinfo->destport, pinfo->srcport, NO_ADDR_B);

    if (p_conv != NULL) {
        /* Checking if the conversation was already framed */
        umts_fp_conversation_info = (umts_fp_conversation_info_t *)conversation_get_proto_data(p_conv, proto_fp);
        if (umts_fp_conversation_info) {
            if (umts_fp_conversation_info->channel == CHANNEL_FACH_FDD) {
                conversation_set_dissector(p_conv, fp_handle);
                dissect_fp(tvb, pinfo, tree, data);
                return TRUE;
            }
            else if (umts_fp_conversation_info->channel != CHANNEL_UNKNOWN){
                /* This conversation was successfuly framed as ANOTHER type */
                return FALSE;
            }
        }
    }
    /* Making sure we have at least enough bytes for header (4) + footer (2) */
    captured_length = tvb_captured_length(tvb);
    if(captured_length < 6) {
        return FALSE;
    }

    /* Expecting specific lengths: 27 for frames with 1 TB, 48 for frames with 2 TBs */
    /* This is a common Transport Format of FACH ( See 3GPP TR 25.944 / 4.1.1.2 'FACH2' ) */
    reported_length = tvb_reported_length(tvb);
    if (reported_length != 27 && reported_length != 48) {
        return FALSE;
    }

    p_fp_info = (fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);

    /* Making sure FP info isn't already attached */
    if (p_fp_info) {
        return FALSE;
    }

    frame_type = tvb_get_guint8(tvb, 0) & 0x01;
    if (frame_type == 1) { /* is 'control' frame type*/
                           /* We can't tell the FP type and content of control frames */
        return FALSE;
    }

    tfi = tvb_get_guint8(tvb, 2) & 0x1f;
    if (reported_length == 27 && tfi != 0x01) {
        return FALSE;
    }
    if (reported_length == 48 && tfi != 0x02) {
        return FALSE;
    }

    tctf = tvb_get_guint8(tvb, 4);
    /* Asserting the TCTF field contains a valid (non reserved) value according to TS 25.321 Table 9.2.1-2 */
    if (tctf != 0x40 && /* CCCH */
        tctf != 0x50 && /* MCCH */
        tctf != 0x5F && /* MSCH */
        tctf != 0x80 && /* CTCH */
        (tctf >> 4) != 0x06 && /* MTCH */
        (tctf >> 6) != 0x00 && /* BCCH */
        (tctf >> 6) != 0x03) { /* DCCH or DTCH over FACH */
        return FALSE;
    }

    if (!check_header_crc_for_heur(tvb, 4)) {
        return FALSE;
    }
    if (!check_payload_crc_for_heur(tvb, 4)) {
        return FALSE;
    }

    if(!umts_fp_conversation_info) {
        umts_fp_conversation_info = wmem_new0(wmem_file_scope(), umts_fp_conversation_info_t);
        set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
    }
    umts_fp_conversation_info->iface_type = IuB_Interface;
    umts_fp_conversation_info->division = Division_FDD;
    umts_fp_conversation_info->dl_frame_number = pinfo->num;
    umts_fp_conversation_info->ul_frame_number = pinfo->num;
    umts_fp_conversation_info->dch_crc_present = 1;
    umts_fp_conversation_info->com_context_id = generate_ue_id_for_heur(pinfo);
    umts_fp_conversation_info->rlc_mode = FP_RLC_AM;
    copy_address_wmem(wmem_file_scope(), &(umts_fp_conversation_info->crnc_address), &pinfo->src);
    umts_fp_conversation_info->crnc_port = pinfo->srcport;
    umts_fp_conversation_info->channel = CHANNEL_FACH_FDD;
    umts_fp_conversation_info->num_dch_in_flow = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].num_dl_chans = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_num_tbs[1] = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_tf_size[1] = 168;
    umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_num_tbs[2] = 2;
    umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_tf_size[2] = 168;
    /* Adding the 'channel specific info' for FACH */
    fp_fach_channel_info = wmem_new0(wmem_file_scope(), fp_fach_channel_info_t);
    fp_fach_channel_info->crnti_to_urnti_map = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
    umts_fp_conversation_info->channel_specific_info = (void*)fp_fach_channel_info;

    conversation_set_dissector(find_or_create_conversation(pinfo), fp_handle);
    dissect_fp(tvb, pinfo, tree, data);
    return TRUE;
}
static gboolean
heur_dissect_fp_rach(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    conversation_t   *p_conv;
    fp_rach_channel_info_t* fp_rach_channel_info;
    umts_fp_conversation_info_t* umts_fp_conversation_info = NULL;
    struct fp_info *p_fp_info;
    guint32 captured_length;
    guint32 reported_length;
    guint8 frame_type;
    guint8 tfi;
    guint8 tctf;

    /* Finding or creating conversation */
    p_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
        conversation_pt_to_endpoint_type(pinfo->ptype),
        pinfo->destport, pinfo->srcport, NO_ADDR_B);

    if (p_conv != NULL) {
        /* Checking if the conversation was already framed */
        umts_fp_conversation_info = (umts_fp_conversation_info_t *)conversation_get_proto_data(p_conv, proto_fp);
        if (umts_fp_conversation_info) {
            if (umts_fp_conversation_info->channel == CHANNEL_RACH_FDD) {
                conversation_set_dissector(p_conv, fp_handle);
                dissect_fp(tvb, pinfo, tree, data);
                return TRUE;
            }
            else if (umts_fp_conversation_info->channel != CHANNEL_UNKNOWN){
                /* This conversation was successfuly framed as ANOTHER type */
                return FALSE;
            }
        }
    }

    /* Making sure we have at least enough bytes for header (4) + footer (2) */
    captured_length = tvb_captured_length(tvb);
    if(captured_length < 6) {
        return FALSE;
    }

    /* Expecting specific lengths: rach frames are either 28 or 52 bytes long */
    /* This is the common Transport Formats of RACH ( See 3GPP TR 25.944 / 4.1.2.1 ) */
    reported_length = tvb_reported_length(tvb);
    if (reported_length != 28 && reported_length != 52) {
        return FALSE;
    }

    p_fp_info = (fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);

    /* Making sure FP info isn't already attached */
    if (p_fp_info) {
        return FALSE;
    }

    frame_type = tvb_get_guint8(tvb, 0) & 0x01;
    if (frame_type == 1) { /* is 'control' frame type*/
                           /* We can't tell the FP type and content of control frames */
        return FALSE;
    }

    tfi = tvb_get_guint8(tvb, 2) & 0x1f;
    if (reported_length == 28 && tfi != 0x00) {
        return FALSE;
    }
    if (reported_length == 52 && tfi != 0x01) {
        return FALSE;
    }

    tctf = tvb_get_guint8(tvb, 4) >> 6;
    /* Asserting the TCTF field contains a valid (non reserved) value according to TS 25.321 Table 9.2.1-4 */
    if (tctf != 0x00 && /* CCCH */
        tctf != 0x01)  /* DCCH over RACH */
    {
        return FALSE;
    }

    if (!check_header_crc_for_heur(tvb, 4)) {
        return FALSE;
    }
    if (!check_payload_crc_for_heur(tvb, 4)) {
        return FALSE;
    }

    if(!umts_fp_conversation_info) {
        umts_fp_conversation_info = wmem_new0(wmem_file_scope(), umts_fp_conversation_info_t);
        set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
    }
    umts_fp_conversation_info->iface_type = IuB_Interface;
    umts_fp_conversation_info->division = Division_FDD;
    umts_fp_conversation_info->dl_frame_number = pinfo->num;
    umts_fp_conversation_info->ul_frame_number = pinfo->num;
    umts_fp_conversation_info->dch_crc_present = 1;
    umts_fp_conversation_info->com_context_id = generate_ue_id_for_heur(pinfo);
    umts_fp_conversation_info->rlc_mode = FP_RLC_AM;
    copy_address_wmem(wmem_file_scope(), &(umts_fp_conversation_info->crnc_address), &pinfo->dst);
    umts_fp_conversation_info->crnc_port = pinfo->destport;
    umts_fp_conversation_info->channel = CHANNEL_RACH_FDD;
    umts_fp_conversation_info->num_dch_in_flow = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].num_ul_chans = 0;
    umts_fp_conversation_info->fp_dch_channel_info[0].ul_chan_num_tbs[0] = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].ul_chan_num_tbs[1] = 1;
    umts_fp_conversation_info->fp_dch_channel_info[0].ul_chan_tf_size[0] = 168;
    umts_fp_conversation_info->fp_dch_channel_info[0].ul_chan_tf_size[1] = 360;

    /* Adding the 'channel specific info' for RACH */
    fp_rach_channel_info = wmem_new0(wmem_file_scope(), fp_rach_channel_info_t);
    fp_rach_channel_info->crnti_to_urnti_map = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
    umts_fp_conversation_info->channel_specific_info = (void*)fp_rach_channel_info;

    conversation_set_dissector(find_or_create_conversation(pinfo), fp_handle);
    dissect_fp(tvb, pinfo, tree, data);
    return TRUE;
}
static gboolean
heur_dissect_fp_pch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    conversation_t   *p_conv;
    umts_fp_conversation_info_t* umts_fp_conversation_info = NULL;
    fp_pch_channel_info_t* fp_pch_channel_info = NULL;
    struct fp_info *p_fp_info;
    gboolean conversation_initialized = FALSE;
    guint32 captured_length;
    guint32 reported_length;
    guint8 frame_type;
    guint8 reserved_bits;
    guint8 tfi;
    guint8 pi_byte_length;
    guint16 tb_byte_length;
    gboolean pi_present;
    gboolean tb_size_found;
    gboolean pi_length_found;
    guint8 cfn_lowest_bits;
    guint8 dch_collisions_byte;

    /* To correctly dissect a PCH stream 2 parameters are required: PI Bitmap length & TB length */
    /* Both are optional in each packet and having them both in a packet without knowing any of them */
    /* is not helpful.*/
    /* Hence gathering the info from 2 different frames is required. */

    /* Finding or creating conversation */
    p_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
        conversation_pt_to_endpoint_type(pinfo->ptype),
        pinfo->destport, pinfo->srcport, NO_ADDR_B);

    if (p_conv != NULL) {
        /* Checking if the conversation was already framed */
        umts_fp_conversation_info = (umts_fp_conversation_info_t *)conversation_get_proto_data(p_conv, proto_fp);
        if (umts_fp_conversation_info) {
            fp_pch_channel_info = (fp_pch_channel_info_t*)umts_fp_conversation_info->channel_specific_info;
            /* Making sure this conversation type is "PCH" and the PCH channel info is present */
            if (umts_fp_conversation_info->channel == CHANNEL_PCH && fp_pch_channel_info != NULL) {
                conversation_initialized = TRUE;
                pi_length_found = fp_pch_channel_info->paging_indications != 0;
                tb_size_found = umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_tf_size[1] != 0;
                if (pi_length_found && tb_size_found) {
                    /* Stream already framed - contains both PI length and TB size */
                    dissect_fp(tvb, pinfo, tree, data);
                    return TRUE;
                }
            }
            else if (umts_fp_conversation_info->channel != CHANNEL_UNKNOWN){
                /* This conversation was successfuly framed as ANOTHER type */
                return FALSE;
            }
            else {
                /* FP conversation info attached and the channel type is UNKNOWN - might be PCH */
                tb_size_found = FALSE;
                pi_length_found = FALSE;
            }
        }
        else {
            /* FP conversation info not attached - no PCH info is known */
            tb_size_found = FALSE;
            pi_length_found = FALSE;
        }
    }
    else {
        /* A conversation does not exist yet - no PCH info is known */
        tb_size_found = FALSE;
        pi_length_found = FALSE;
    }

    /* Making sure we have at least enough bytes for header (4) + footer (2) */
    captured_length = tvb_captured_length(tvb);
    if(captured_length < 6) {
        return FALSE;
    }

    p_fp_info = (fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);
    /* Making sure FP info isn't already attached */
    if (p_fp_info) {
        return FALSE;
    }

    frame_type = tvb_get_guint8(tvb, 0) & 0x01;
    if (frame_type == 1) { /* is 'control' frame type*/
                           /* We can't tell the FP type and content of control frames */
        return FALSE;
    }

    /* Checking bits after CFN and before PI indicator are zeroed */
    reserved_bits = tvb_get_guint8(tvb, 2) & 0x0E;
    if (reserved_bits != 0x00) {
        return FALSE;
    }

    tfi = tvb_get_guint8(tvb, 3) & 0x1f;
    if (tfi != 0x00 && tfi != 0x01) {
        return FALSE;
    }

    if (!check_header_crc_for_heur(tvb, 4)) {
        return FALSE;
    }
    if (!check_payload_crc_for_heur(tvb, 4)) {
        return FALSE;
    }

    reported_length = tvb_reported_length(tvb);
    pi_present = tvb_get_guint8(tvb, 2) & 0x01; /* Rightmost bit in the 3rd byte */
    if (pi_present) {
        if (tfi == 0x00 && !pi_length_found) {
            /* PI Bitmap present and No TB. Can calculate PI bitmap length */
            guint8 pi_bit_length;
            pi_byte_length = reported_length - 6; /* Removing header length (4) and footer length (2)*/
            switch (pi_byte_length)
            {
            case 3: /* 18 bits bitmap + padding */
                pi_bit_length = 18;
                break;
            case 5: /* 36 bits bitmap + padding */
                pi_bit_length = 36;
                break;
            case 9: /* 72 bits bitmap */
                pi_bit_length = 72;
                break;
            case 18: /* 144 bits bitmap */
                pi_bit_length = 144;
                break;
            default:
                return FALSE;
            }

            if (pi_bit_length == 144 && !tb_size_found) {
                /* Nothing has confirmed yet that this channel is a PCH since */
                /* both 'tb_size_found' and 'pi_length_found' are false. */
                /* Checking if the 4 LSB bits of the CFN (the 4 leftmost bits in the 3rd byte) aren't zeroed. */
                /* if they aren't this is probably PCH because those are reserved in DCH */
                cfn_lowest_bits = tvb_get_guint8(tvb, 2) & 0xF0;
                if(cfn_lowest_bits == 0) {
                    /* Checking if the 4th byte in the frame is zeroed. In this case the CRC checks aren't */
                    /* deterministic enough to gurantee this is a PCH since this packet could also be a DCH frame */
                    /* with MAC's C/T is 0 and 4 leftmost bits of RLC are 0 */
                    dch_collisions_byte = tvb_get_guint8(tvb, 3);
                    if (dch_collisions_byte == 0) {
                        return FALSE;
                    }
                }
            }

            if (!umts_fp_conversation_info) {
                umts_fp_conversation_info = wmem_new0(wmem_file_scope(), umts_fp_conversation_info_t);
                set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
            }
            if(!conversation_initialized) {
                fill_pch_conversation_info_for_heur(umts_fp_conversation_info, pinfo);
                fp_pch_channel_info = (fp_pch_channel_info_t*)umts_fp_conversation_info->channel_specific_info;
            }
            fp_pch_channel_info->paging_indications = pi_bit_length;
            pi_length_found = TRUE;
        }
        else if (tfi == 0x01 && !tb_size_found && pi_length_found) {
            /* TB present and PI bitmap length is known. Can calculate TB length.*/
            pi_byte_length = (fp_pch_channel_info->paging_indications + 7) / 8;
            if (!umts_fp_conversation_info) {
                umts_fp_conversation_info = wmem_new0(wmem_file_scope(), umts_fp_conversation_info_t);
                set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
            }
            if(!conversation_initialized) {
                fill_pch_conversation_info_for_heur(umts_fp_conversation_info, pinfo);
            }
            tb_byte_length = (reported_length - (pi_byte_length + 6)); /* Removing header length (4), footer length (2) and PI bitmap length*/
            /* Possible TB lengths for PCH is 10 or 30 bytes ( See 3GPP TR 25.944 / 4.1.1.2 ) */
            if (tb_byte_length == 10 || tb_byte_length == 30) {
                umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_tf_size[1] = tb_byte_length * 8;
                tb_size_found = TRUE;
            }
        }
        /* TODO: It should be possible to figure both PI & TB sizes if both are present in a frame and neither is known */
        /* Since the total size of the frame should be unique */
        /* e.g. 19 bytes = header (4) + PI 18bits (3) + TB (10) + footer (2)*/
        /*      21 bytes = header (4) + PI 36bits (5) + TB (10) + footer (2)*/
        /*      etc... */
        /* This could mostly help dissect 'busy' PCHs where most of the frames have both PI & TB*/
    }
    else {
        if (tfi == 0x01 && !tb_size_found) {
            /* TB present and PI bitmap is missing. Can calculate TB length.*/
            if (!umts_fp_conversation_info) {
                umts_fp_conversation_info = wmem_new0(wmem_file_scope(), umts_fp_conversation_info_t);
                set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
            }
            if(!conversation_initialized) {
                fill_pch_conversation_info_for_heur(umts_fp_conversation_info, pinfo);
            }
            tb_byte_length = (reported_length - 6); /* Removing header length (4), footer length (2) */
            /* Possible TB lengths for PCH is 10 or 30 bytes ( See 3GPP TR 25.944 / 4.1.1.2 ) */
            if (tb_byte_length == 10 || tb_byte_length == 30) {
                umts_fp_conversation_info->fp_dch_channel_info[0].dl_chan_tf_size[1] = tb_byte_length * 8;
                set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
                tb_size_found = TRUE;
            }
        }
    }

    if (pi_length_found && tb_size_found) {
        /* Stream completely framed! */
        conversation_set_dissector(find_or_create_conversation(pinfo), fp_handle);
        dissect_fp(tvb, pinfo, tree, data);
        return TRUE;
    }
    else {
        /* Some data still missing */
        return FALSE;
    }
}
static gboolean
heur_dissect_fp_hsdsch_type_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    conversation_t   *p_conv;
    umts_fp_conversation_info_t* umts_fp_conversation_info = NULL;
    fp_hsdsch_channel_info_t* fp_hsdsch_channel_info;
    struct fp_info *p_fp_info;
    guint32 captured_length;
    guint32 reported_length;
    guint8 frame_type;
    guint16 mac_d_pdu_length;
    guint16 num_of_pdus;
    guint32 expected_total_size;
    guint32 next_pdu_index;
    guint16 index_step;
    guint8 pre_pdu_padding;

    /* Trying to find existing conversation */
    p_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
        conversation_pt_to_endpoint_type(pinfo->ptype),
        pinfo->destport, pinfo->srcport, NO_ADDR_B);

    if (p_conv != NULL) {
        /* Checking if the conversation was already framed */
        umts_fp_conversation_info = (umts_fp_conversation_info_t *)conversation_get_proto_data(p_conv, proto_fp);
        if (umts_fp_conversation_info) {
            fp_hsdsch_channel_info = (fp_hsdsch_channel_info_t*)umts_fp_conversation_info->channel_specific_info;
            if (umts_fp_conversation_info->channel == CHANNEL_HSDSCH && fp_hsdsch_channel_info->hsdsch_entity == hs) {
                conversation_set_dissector(p_conv, fp_handle);
                dissect_fp(tvb, pinfo, tree, data);
                return TRUE;
            }
            else if (umts_fp_conversation_info->channel != CHANNEL_UNKNOWN){
                /* This conversation was successfuly framed as ANOTHER type */
                return FALSE;
            }
        }
    }

    /* Making sure FP info isn't already attached */
    p_fp_info = (fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);
    if (p_fp_info) {
        return FALSE;
    }

    captured_length = tvb_reported_length(tvb);
    /* Lengths limit: header size (7) + at least 1 PDU Block (2) + CRC Payload size (2)*/
    if (captured_length < 11) {
        return FALSE;
    }

    frame_type = tvb_get_guint8(tvb, 0) & 0x01;
    if (frame_type == 1) { /* is 'control' frame type*/
        return FALSE;
    }

    /* Lengths limit: Smallest HS-DSCH type 1 data frame is 55 bytes (1 PDU of 336 bits) */
    reported_length = tvb_reported_length(tvb);
    if (reported_length < 55) {
        return FALSE;
    }

    mac_d_pdu_length = tvb_get_guint16(tvb, 2, ENC_NA) >> 3;
    /* Only valid PDU lengths are 336 or 656 */
    if (mac_d_pdu_length != 336 && mac_d_pdu_length != 656) {
        return FALSE;
    }

    num_of_pdus = tvb_get_guint8(tvb, 4);
    /* PDUs count shouldn't be 0*/
    if (num_of_pdus == 0) {
        return FALSE;
    }
    /* Maximum PDUs count constraint: 32 PDUs * 336 bits or 17 PDUs * 656 bits */
    if ((mac_d_pdu_length == 336 && num_of_pdus > 32) || (mac_d_pdu_length == 656 && num_of_pdus > 17)) {
        return FALSE;
    }

    /* Making sure the expected packet size is smaller/equals to the entire packet's size */
    expected_total_size = (num_of_pdus * mac_d_pdu_length / 8) + 7 /*Header length*/ + 2 /*Footer length*/;
    if (expected_total_size > captured_length || expected_total_size > reported_length) {
        return FALSE;
    }

    /* Iterating through the PDUs making sure the padding nibble is present in all of them */
    next_pdu_index = 7;
    index_step = mac_d_pdu_length / 8;
    for (int i = 0; i < num_of_pdus; i++)
    {
        pre_pdu_padding = tvb_get_guint8(tvb, next_pdu_index) >> 4;
        if (pre_pdu_padding != 0x00)
        {
            /* One of the padding nibbles is not zeroed */
            return FALSE;
        }
        next_pdu_index += index_step;
    }

    if (!check_header_crc_for_heur(tvb, 7)) {
        return FALSE;
    }
    if (!check_payload_crc_for_heur(tvb, 7)) {
        return FALSE;
    }

    if(!umts_fp_conversation_info) {
        umts_fp_conversation_info = wmem_new0(wmem_file_scope(), umts_fp_conversation_info_t);
        set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
    }
    umts_fp_conversation_info->iface_type = IuB_Interface;
    umts_fp_conversation_info->division = Division_FDD;
    umts_fp_conversation_info->dl_frame_number = pinfo->num;
    umts_fp_conversation_info->ul_frame_number = pinfo->num;
    umts_fp_conversation_info->dch_crc_present = 1;
    umts_fp_conversation_info->com_context_id = generate_ue_id_for_heur(pinfo);
    umts_fp_conversation_info->rlc_mode = FP_RLC_AM;
    copy_address_wmem(wmem_file_scope(), &(umts_fp_conversation_info->crnc_address), &pinfo->src);
    umts_fp_conversation_info->crnc_port = pinfo->srcport;
    umts_fp_conversation_info->channel = CHANNEL_HSDSCH;
    fp_hsdsch_channel_info = wmem_new0(wmem_file_scope(), fp_hsdsch_channel_info_t);
    fp_hsdsch_channel_info->hsdsch_entity = hs;
    fp_hsdsch_channel_info->hsdsch_macdflow_id = 0;
    umts_fp_conversation_info->channel_specific_info = (void*)fp_hsdsch_channel_info;

    conversation_set_dissector(find_or_create_conversation(pinfo), fp_handle);
    dissect_fp(tvb, pinfo, tree, data);
    return TRUE;
}
static gboolean
heur_dissect_fp_hsdsch_type_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    conversation_t   *p_conv;
    umts_fp_conversation_info_t* umts_fp_conversation_info = NULL;
    fp_hsdsch_channel_info_t* fp_hsdsch_channel_info;
    struct fp_info *p_fp_info;
    guint32 captured_length;
    guint32 reported_length;
    guint8 frame_type;
    guint8 reserved_fach_ind_bits;
    guint8 pdu_block_header_reserved_bit;
    guint8 pdu_block_headers_count;
    guint16 next_pdu_block_header_index;
    guint16 pdu_block_header_pdu_length;
    guint8 pdu_block_header_pdus_count;
    guint8 pdu_block_header_lchid;
    guint32 total_header_length;
    guint32 expected_payload_length;

    /* Trying to find existing conversation */
    p_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
        conversation_pt_to_endpoint_type(pinfo->ptype),
        pinfo->destport, pinfo->srcport, NO_ADDR_B);

    if (p_conv != NULL) {
        /* Checking if the conversation was already framed */
        umts_fp_conversation_info = (umts_fp_conversation_info_t *)conversation_get_proto_data(p_conv, proto_fp);
        if (umts_fp_conversation_info) {
            fp_hsdsch_channel_info = (fp_hsdsch_channel_info_t*)umts_fp_conversation_info->channel_specific_info;
            if (umts_fp_conversation_info->channel == CHANNEL_HSDSCH && fp_hsdsch_channel_info->hsdsch_entity == ehs) {
                conversation_set_dissector(p_conv, fp_handle);
                dissect_fp(tvb, pinfo, tree, data);
                return TRUE;
            }
            else if (umts_fp_conversation_info->channel != CHANNEL_UNKNOWN){
                /* This conversation was successfuly framed as ANOTHER type */
                return FALSE;
            }
        }
    }

    /* Making sure FP info isn't already attached */
    p_fp_info = (fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);
    if (p_fp_info) {
        return FALSE;
    }

    captured_length = tvb_captured_length(tvb);
    reported_length = tvb_reported_length(tvb);
    /* Lengths limit: header size + at least 1 PDU Block Header + CRC Payload size */
    if (captured_length < 11) {
        return FALSE;
    }

    frame_type = tvb_get_guint8(tvb, 0) & 0x01;
    if (frame_type == 1) { /* is 'control' frame type*/
        return FALSE;
    }

    pdu_block_header_reserved_bit = (tvb_get_guint8(tvb, 7) & 0x10) >> 4;
    if (pdu_block_header_reserved_bit == 0x1) {
        return FALSE;
    }

    /* Expecting at least 1 PDU Block Header */
    pdu_block_headers_count = tvb_get_guint8(tvb, 2) >> 3;
    if (pdu_block_headers_count == 0) {
        return FALSE;
    }

    /* Getting 3 rightmost bits in the FACH Indicator's byte, which are reserved and should be 0 */
    reserved_fach_ind_bits = tvb_get_guint8(tvb, 3) & 0x03;
    if (reserved_fach_ind_bits != 0x00) {
        return FALSE;
    }

    /* Iterating through the block headers looking for invalid fields and */
    /* calculating the expected total packet length */
    total_header_length = 6;
    expected_payload_length = 0;
    for (int i = 0; i < pdu_block_headers_count; i++)
    {
        /* Making sure the next index is not out of range */
        if (((guint32)(8 + (i * 3))) >= captured_length) {
            return FALSE;
        }

        /* Getting blocks length and count from the i-th header */
        if (i % 2 == 0) {
            next_pdu_block_header_index = (i * 25) / 10;
        }
        else {
            next_pdu_block_header_index = (((i-1) * 25) / 10) + 2;
        }
        pdu_block_header_pdu_length = tvb_get_guint16(tvb, 6 + next_pdu_block_header_index, ENC_NA) >> 5;
        pdu_block_header_pdus_count = tvb_get_guint8(tvb, 7 + next_pdu_block_header_index) & 0x0F;
        pdu_block_header_lchid = tvb_get_guint8(tvb, 8 + next_pdu_block_header_index) >> 4;


        /* Making sure PDUs' Length isn't zeroed*/
        if (pdu_block_header_pdu_length == 0) {
            return FALSE;
        }
        /* Making sure PDUs Count isn't zeroed */
        if (pdu_block_header_pdus_count == 0) {
            return FALSE;
        }

        /* Adding this header's length to expected length*/
        if (i % 2 == 0) {
            total_header_length += 3;
        }
        else {
            total_header_length += 2;
        }
        /* Adding this header's paylod's size to expected length*/
        expected_payload_length += (pdu_block_header_pdu_length * pdu_block_header_pdus_count);

        /* Checking padding after lchid */
        if ((tvb_get_guint8(tvb, 8 + (i * 3)) & 0x0F) != 0x00) {
            return FALSE;
        }
        /* Checking lchid for reserved value 0x0F*/

        if (pdu_block_header_lchid == 0x0F) {
            return FALSE;
        }
    }
    /* Adding Payload CRC'slength to payload length*/
    expected_payload_length += 2;
    /* Calculated expected packet size must not exceed captured length or reported length*/
    if ((total_header_length + expected_payload_length) > captured_length || (total_header_length + expected_payload_length) > reported_length) {
        return FALSE;
    }

    if (!check_header_crc_for_heur(tvb, total_header_length)) {
        return FALSE;
    }
    if (!check_payload_crc_for_heur(tvb, total_header_length)) {
        return FALSE;
    }

    if(!umts_fp_conversation_info) {
        umts_fp_conversation_info = wmem_new0(wmem_file_scope(), umts_fp_conversation_info_t);
        set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
    }
    umts_fp_conversation_info->iface_type = IuB_Interface;
    umts_fp_conversation_info->division = Division_FDD;
    umts_fp_conversation_info->dl_frame_number = pinfo->num;
    umts_fp_conversation_info->ul_frame_number = pinfo->num;
    umts_fp_conversation_info->dch_crc_present = 1;
    umts_fp_conversation_info->com_context_id = generate_ue_id_for_heur(pinfo);
    umts_fp_conversation_info->rlc_mode = FP_RLC_AM;
    copy_address_wmem(wmem_file_scope(), &(umts_fp_conversation_info->crnc_address), &pinfo->src);
    umts_fp_conversation_info->crnc_port = pinfo->srcport;
    umts_fp_conversation_info->channel = CHANNEL_HSDSCH;
    fp_hsdsch_channel_info = wmem_new0(wmem_file_scope(), fp_hsdsch_channel_info_t);
    fp_hsdsch_channel_info->hsdsch_entity = ehs;
    fp_hsdsch_channel_info->hsdsch_macdflow_id = 1;
    umts_fp_conversation_info->channel_specific_info = (void*)fp_hsdsch_channel_info;

    conversation_set_dissector(find_or_create_conversation(pinfo), fp_handle);
    dissect_fp(tvb, pinfo, tree, data);
    return TRUE;
}

static gboolean
heur_dissect_fp_edch_type_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    conversation_t   *p_conv;
    umts_fp_conversation_info_t* umts_fp_conversation_info = NULL;
    fp_edch_channel_info_t* fp_edch_channel_info;
    struct fp_info *p_fp_info;
    guint32 captured_length;
    guint8 frame_type;
    guint8 num_sub_frames_byte;
    guint8 number_of_subframes;
    guint8 number_of_mac_es_pdus;
    guint32 subframe_number;
    guint32 total_sub_headers_len;
    guint32 total_header_length;
    guint32 payload_length;
    guint32 total_mac_pdus_count;
    guint32 macd_pdu_bit_size;
    guint32 bit_offset;
    guint32 offset;
    guint32 i = 0;
    guint32 n = 0;

    /* Trying to find existing conversation */
    p_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
        conversation_pt_to_endpoint_type(pinfo->ptype),
        pinfo->destport, pinfo->srcport, NO_ADDR_B);

    if (p_conv != NULL) {
        /* Checking if the conversation was already framed */
        umts_fp_conversation_info = (umts_fp_conversation_info_t *)conversation_get_proto_data(p_conv, proto_fp);
        if (umts_fp_conversation_info) {
            fp_edch_channel_info = (fp_edch_channel_info_t*)umts_fp_conversation_info->channel_specific_info;
            if (umts_fp_conversation_info->channel == CHANNEL_EDCH && fp_edch_channel_info->edch_type == 0) {
                conversation_set_dissector(p_conv, fp_handle);
                dissect_fp(tvb, pinfo, tree, data);
                return TRUE;
            }
            else if (umts_fp_conversation_info->channel != CHANNEL_UNKNOWN){
                /* This conversation was successfuly framed as ANOTHER type */
                return FALSE;
            }
        }
    }

    /* Making sure FP info isn't already attached */
    p_fp_info = (fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);
    if (p_fp_info) {
        return FALSE;
    }

    captured_length = tvb_reported_length(tvb);
    /* Lengths limit: header size + at least 1 Subframe Header + CRC Payload size */
    if (captured_length < 9) {
        return FALSE;
    }

    frame_type = tvb_get_guint8(tvb, 0) & 0x01;
    if (frame_type == 1) { /* is 'control' frame type*/
        return FALSE;
    }

    num_sub_frames_byte = tvb_get_guint8(tvb, 2);
    /* Checking 4 leftmost bits in the 'Number of Subframes' byte, which are reserved and should be 0 */
    if (num_sub_frames_byte & 0xf0) {
        return FALSE;
    }

    /* Values {11-16} are reserved */
    number_of_subframes = (num_sub_frames_byte & 0x0f) + 1;
    if (number_of_subframes >= 11) {
        return FALSE;
    }

    /* Iterating through the block headers looking for invalid fields */
    total_header_length = 4;
    offset = 4;
    total_mac_pdus_count = 0;
    /* EDCH subframe header list */
    for (n=0; n < number_of_subframes; n++) {

        /* Making sure the next index is not out of range */
        if (((guint32)(offset + 3)) >= captured_length) {
            return FALSE;
        }

        /* Subframe number */
        subframe_number = (tvb_get_guint8(tvb, offset) & 0x07);
        if (subframe_number > 4) {
            return FALSE;
        }
        offset++;

        /* Number of MAC-es PDUs */
        number_of_mac_es_pdus = (tvb_get_guint8(tvb, offset) & 0xf0) >> 4;
        if (number_of_mac_es_pdus == 0) {
            return FALSE;
        }
        bit_offset = 4;

        /* Making sure enough bytes are presesnt for all sub-header */
        total_sub_headers_len = ((int)((((1.5 + (number_of_mac_es_pdus * 1.5))*8+7)/8)));
        if ((offset + total_sub_headers_len) >= captured_length) {
            return FALSE;
        }
        /* Details of each MAC-es PDU */
        for (i=0; i < number_of_mac_es_pdus; i++) {
            guint32 n_pdus;    /*Size of the PDU*/

            /* DDI (6 bits) */
            bit_offset += 6;

            /* Number of MAC-d PDUs (6 bits) */
            n_pdus = tvb_get_bits8( tvb, offset*8 + bit_offset, 6);
            total_mac_pdus_count += n_pdus;
            bit_offset += 6;
        }

        total_header_length += total_sub_headers_len;
        offset += ((bit_offset+7)/8);
    }

    /* Figure MAC bit size */
    payload_length = captured_length - total_header_length - 3; /* Removing 3 bytes for Payload CRC and TSN */
    if (payload_length == (total_mac_pdus_count * 42)) {
        macd_pdu_bit_size = 336;
    }
    else if (payload_length == (total_mac_pdus_count * 18)) {
        macd_pdu_bit_size = 144;
    }
    else {
        /* Unexpected payload length or DDIs combination */
        return FALSE;
    }

    if (!check_edch_header_crc_for_heur(tvb, total_header_length)) {
        return FALSE;
    }
    if (!check_payload_crc_for_heur(tvb, total_header_length)) {
        return FALSE;
    }

    if(!umts_fp_conversation_info) {
        umts_fp_conversation_info = wmem_new0(wmem_file_scope(), umts_fp_conversation_info_t);
        set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
    }
    umts_fp_conversation_info->iface_type = IuB_Interface;
    umts_fp_conversation_info->division = Division_FDD;
    umts_fp_conversation_info->dl_frame_number = pinfo->num;
    umts_fp_conversation_info->ul_frame_number = pinfo->num;
    umts_fp_conversation_info->dch_crc_present = 1;
    umts_fp_conversation_info->com_context_id = generate_ue_id_for_heur(pinfo);
    umts_fp_conversation_info->rlc_mode = FP_RLC_AM;
    copy_address_wmem(wmem_file_scope(), &(umts_fp_conversation_info->crnc_address), &pinfo->src);
    umts_fp_conversation_info->crnc_port = pinfo->srcport;
    umts_fp_conversation_info->channel = CHANNEL_EDCH;
    fp_edch_channel_info = wmem_new0(wmem_file_scope(), fp_edch_channel_info_t);
    fp_edch_channel_info->no_ddi_entries = 0x0f;
    for(i = 0;i<0x0f;i++) {
        fp_edch_channel_info->edch_ddi[i] = i;
        fp_edch_channel_info->edch_macd_pdu_size[i] = macd_pdu_bit_size;
        fp_edch_channel_info->edch_lchId[i] = 9;
    }
    fp_edch_channel_info->edch_type = 0; /* Type 1 */
    umts_fp_conversation_info->channel_specific_info = (void*)fp_edch_channel_info;

    conversation_set_dissector(find_or_create_conversation(pinfo), fp_handle);
    dissect_fp(tvb, pinfo, tree, data);
    return TRUE;
}
/* This method can frame UDP streams containing FP packets but dissection of those packets will */
/* fail since the FP conversation info is never attached */
/* Usefull for DCH streams containing CS data and don't have their own heuristic method */
static gboolean
heur_dissect_fp_unknown_format(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    conversation_t   *p_conv;
    umts_fp_conversation_info_t* umts_fp_conversation_info = NULL;
    struct fp_info *p_fp_info;
    guint32 length;
    guint8 frame_type;
    gboolean is_control = FALSE;

    /* Trying to find existing conversation */
    p_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
        conversation_pt_to_endpoint_type(pinfo->ptype),
        pinfo->destport, pinfo->srcport, NO_ADDR_B);

    /* Check if FP Conversation Info is attached */
    if (p_conv != NULL) {
        umts_fp_conversation_info = (umts_fp_conversation_info_t *)conversation_get_proto_data(p_conv, proto_fp);
        if (umts_fp_conversation_info) {
            if (umts_fp_conversation_info->channel == CHANNEL_UNKNOWN) {
                /* This stream was framed using a previous control frame, we can call FP dissector without further tests*/
                dissect_fp(tvb, pinfo, tree, data);
                return TRUE;
            }
            else {
                return FALSE;
            }
        }
    }

    p_fp_info = (fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);

    /* Check if per-frame FP Info is attached*/
    if(p_fp_info) {
        /* if FP info is present, check that it really is an ethernet link */
        if (p_fp_info->link_type != FP_Link_Ethernet) {
            return FALSE;
        }

        /* discriminate 'lower' UDP layer from 'user data' UDP layer
         * (i.e. if an FP over UDP packet contains a user UDP packet */
        if (p_fp_info->srcport != pinfo->srcport ||
            p_fp_info->destport != pinfo->destport)
            return FALSE;

        /* assume this is FP */
        dissect_fp(tvb, pinfo, tree, data);
        return TRUE;
    }

    /* Both per-frame FP info and conversation FP info are missing */
    /* Try to frame control frames using header CRC */
    is_control = (tvb_get_guint8(tvb, 0) & 0x01) == 1;
    if(!is_control) {
        /* This is a Data frame, can't tell if it's FP. */
        return FALSE;
    }

    length = tvb_captured_length(tvb);
    /* Length limit: control frames header is 2 bytes */
    if (length < 2) {
        return FALSE;
    }

    /* Check 'Frame Type' */
    frame_type = tvb_get_guint8(tvb, 1);
    /* 0x00 is unused for both dedicated & common FP */
    if( frame_type == 0x00 ) {
        return FALSE;
    }
    /* Max frame types are: */
    /* For common channels: 0x0E */
    /* For dedicated channels: 0x0B */
    /* The left nibble is zeroed in both cases */
    if( (frame_type & 0xF0) != 0x00) {
        return FALSE;
    }

    /* Checking Header CRC*/
    if (!check_control_frame_crc_for_heur(tvb)) {
        /* The CRC is incorrect */
        return FALSE;
    }

    /* The CRC is correct! */
    /* Attaching 'FP Conversation Info' the the UDP conversation so other */
    /* packets (both Control AND Data) will be marked as FP */
    umts_fp_conversation_info = wmem_new0(wmem_file_scope(), umts_fp_conversation_info_t);
    umts_fp_conversation_info->channel = CHANNEL_UNKNOWN;
    set_both_sides_umts_fp_conv_data(pinfo, umts_fp_conversation_info);
    /* Call FP Dissector for the current frame */
    dissect_fp(tvb, pinfo, tree, data);
    return TRUE;
}

/* This method wraps the heuristic dissectors of all supported channels */
static gboolean
heur_dissect_fp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    gboolean match;

    match = heur_dissect_fp_dcch_over_dch(tvb, pinfo, tree, data);
    if(match)
        return TRUE;
    match = heur_dissect_fp_fach1(tvb, pinfo, tree, data);
    if(match)
        return TRUE;
    match = heur_dissect_fp_fach2(tvb, pinfo, tree, data);
    if(match)
        return TRUE;
    match = heur_dissect_fp_rach(tvb, pinfo, tree, data);
    if(match)
        return TRUE;
    match = heur_dissect_fp_pch(tvb, pinfo, tree, data);
    if(match)
        return TRUE;
    match = heur_dissect_fp_hsdsch_type_1(tvb, pinfo, tree, data);
    if(match)
        return TRUE;
    match = heur_dissect_fp_hsdsch_type_2(tvb, pinfo, tree, data);
    if(match)
        return TRUE;
    match = heur_dissect_fp_edch_type_1(tvb, pinfo, tree, data);
    if(match)
        return TRUE;
    /* NOTE: Add new heuristic dissectors BEFORE the 'unknown format' dissector */
    /* since it might 'swallow' packets if the UDP stream is framed as 'CHANNEL_UNKNOWN' */
    match = heur_dissect_fp_unknown_format(tvb, pinfo, tree, data);
    if(match)
        return TRUE;

    return FALSE;
}

static guint8 fakes =5; /*[] ={1,5,8};*/
static guint8 fake_map[256];

 /*
 * TODO: This need to be fixed!
 * Basically you would want the actual RRC messages, that sooner or later maps
 * transport channel id's to logical id's or RAB IDs
 * to set the proper logical channel/RAB ID, but for now we make syntethic ones.
 * */

static guint8
make_fake_lchid(packet_info *pinfo _U_, gint trchld)
{
    if ( fake_map[trchld] == 0) {
        fake_map[trchld] = fakes;
        fakes++;
    }
    return fake_map[trchld];
}

/* Tries to resolve the U-RNTI of a channel user based on info in the fp conv info */
static void fp_conv_resolve_urnti(umts_fp_conversation_info_t *p_conv_data)
{
    /* Trying to resolve the U-RNTI of the user if missing */
    /* Resolving based on the 'C-RNC Communication Context' field found in NBAP */
    if (!p_conv_data->urnti && p_conv_data->com_context_id != 0) {
        guint32 * mapped_urnti = (guint32 *)(wmem_tree_lookup32(nbap_crncc_urnti_map,p_conv_data->com_context_id));
        if (mapped_urnti != 0) {
            p_conv_data->urnti = GPOINTER_TO_UINT(mapped_urnti);
        }
    }
}

/* Figures the best "UE ID" to use in RLC reassembly logic */
static guint32 get_ue_id_from_conv(umts_fp_conversation_info_t *p_conv_data)
{
    guint32 user_identity;
    /* Choosing RLC 'UE ID': */
    /* 1. Preferring the U-RNTI if attached */
    /* 2. Fallback - Using the 'C-RNC Communication Context' used in NBAP for this user */
    user_identity = p_conv_data->com_context_id;
    if(p_conv_data->urnti) {
        user_identity = p_conv_data->urnti;
    }
    return user_identity;
}

static fp_info *
fp_set_per_packet_inf_from_conv(conversation_t *p_conv,
                                umts_fp_conversation_info_t *p_conv_data,
                                tvbuff_t *tvb, packet_info *pinfo,
                                proto_tree *tree _U_)
{
    fp_info  *fpi;
    guint8    tfi, c_t, lchid;
    int       offset = 0, i=0, j=0, num_tbs, chan, tb_size, tb_bit_off;
    gboolean  is_control_frame;
    gboolean  is_known_dcch_tf,is_stndalone_ps_rab_tf,is_muxed_cs_ps_tf;
    umts_mac_info *macinf;
    rlc_info *rlcinf;
    guint8 fake_lchid=0;
    gint *cur_val=NULL;
    fp_hsdsch_channel_info_t* fp_hsdsch_channel_info = NULL;
    fp_edch_channel_info_t* fp_edch_channel_info = NULL;
    fp_pch_channel_info_t *fp_pch_channel_info = NULL;
    fp_fach_channel_info_t* fp_fach_channel_info = NULL;
    fp_rach_channel_info_t* fp_rach_channel_info = NULL;
    gboolean info_missing = FALSE;

    fpi = wmem_new0(wmem_file_scope(), fp_info);
    p_add_proto_data(wmem_file_scope(), pinfo, proto_fp, 0, fpi);

    fpi->iface_type = p_conv_data->iface_type;
    fpi->division = p_conv_data->division;
    fpi->release = 7;               /* Set values greater then the checks performed */
    fpi->release_year = 2006;
    fpi->release_month = 12;
    fpi->channel = p_conv_data->channel;
    fpi->dch_crc_present = p_conv_data->dch_crc_present;
    fpi->link_type = FP_Link_Ethernet;

#if 0
    /*Only do this the first run, signals that we need to reset the RLC fragtable*/
    if (!PINFO_FD_VISITED(pinfo) &&  p_conv_data->reset_frag ) {
        fpi->reset_frag = p_conv_data->reset_frag;
        p_conv_data->reset_frag = FALSE;
    }
#endif
    /* remember 'lower' UDP layer port information so we can later
     * differentiate 'lower' UDP layer from 'user data' UDP layer */
    fpi->srcport = pinfo->srcport;
    fpi->destport = pinfo->destport;

    fpi->com_context_id = p_conv_data->com_context_id;
    if(!p_conv_data->urnti) {
        fp_conv_resolve_urnti(p_conv_data);
    }
    fpi->urnti = p_conv_data->urnti;

    if (pinfo->link_dir == P2P_DIR_UL) {
        fpi->is_uplink = TRUE;
    } else {
        fpi->is_uplink = FALSE;
    }

    is_control_frame = tvb_get_guint8(tvb, offset) & 0x01;

    switch (fpi->channel) {
        case CHANNEL_HSDSCH: /* HS-DSCH - High Speed Downlink Shared Channel */
            fp_hsdsch_channel_info = (fp_hsdsch_channel_info_t*)p_conv_data->channel_specific_info;
            if(fp_hsdsch_channel_info == NULL) {
                proto_tree_add_expert_format(tree, pinfo, &ei_fp_no_per_conv_channel_info, tvb, offset, -1,
                                      "Can't dissect HS-DSCH FP stream because no per-conversation channel info was attached!");
                info_missing = TRUE;
                break;
            }
            fpi->hsdsch_entity = fp_hsdsch_channel_info->hsdsch_entity;
            fpi->hsdsch_rlc_mode = p_conv_data->rlc_mode;
            macinf = wmem_new0(wmem_file_scope(), umts_mac_info);
            fpi->hsdsch_macflowd_id = fp_hsdsch_channel_info->hsdsch_macdflow_id;
            macinf->content[0] = hsdsch_macdflow_id_mac_content_map[fp_hsdsch_channel_info->hsdsch_macdflow_id];
            macinf->lchid[0] = fp_hsdsch_channel_info->hsdsch_macdflow_id;
            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0, macinf);

            rlcinf = wmem_new0(wmem_file_scope(), rlc_info);

            /*Figure out RLC_MODE based on MACd-flow-ID, basically MACd-flow-ID = 0 then it's SRB0 == UM else AM*/
            rlcinf->mode[0] = hsdsch_macdflow_id_rlc_map[fp_hsdsch_channel_info->hsdsch_macdflow_id];

            if (fpi->hsdsch_entity == hs) {
                for (i=0; i<MAX_NUM_HSDHSCH_MACDFLOW; i++) {
                    /*Figure out if this channel is multiplexed (signaled from RRC)*/
                    if ((cur_val=(gint *)g_tree_lookup(hsdsch_muxed_flows, GINT_TO_POINTER((gint)fp_hsdsch_channel_info->hrnti))) != NULL) {
                        j = 1 << i;
                        fpi->hsdhsch_macfdlow_is_mux[i] = j & *cur_val;
                    } else {
                        fpi->hsdhsch_macfdlow_is_mux[i] = FALSE;
                    }

                }
            }
            rlcinf->ueid[0] = get_ue_id_from_conv(p_conv_data);
            rlcinf->li_size[0] = RLC_LI_7BITS;
            rlcinf->ciphered[0] = FALSE;
            rlcinf->deciphered[0] = FALSE;
            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0, rlcinf);

            return fpi;

        case CHANNEL_EDCH:
            fp_edch_channel_info = (fp_edch_channel_info_t*)p_conv_data->channel_specific_info;
            if(fp_edch_channel_info == NULL) {
                proto_tree_add_expert_format(tree, pinfo, &ei_fp_no_per_conv_channel_info, tvb, offset, -1,
                                      "Can't dissect E-DCH FP stream because no per-conversation channel info was attached!");
                info_missing = TRUE;
                break;
            }
            macinf = wmem_new0(wmem_file_scope(), umts_mac_info);
            rlcinf = wmem_new0(wmem_file_scope(), rlc_info);
            fpi->no_ddi_entries = fp_edch_channel_info->no_ddi_entries;
            for (i=0; i<fpi->no_ddi_entries; i++) {
                fpi->edch_ddi[i] = fp_edch_channel_info->edch_ddi[i];    /*Set the DDI value*/
                fpi->edch_macd_pdu_size[i] = fp_edch_channel_info->edch_macd_pdu_size[i];    /*Set the PDU size*/
                fpi->edch_lchId[i] = fp_edch_channel_info->edch_lchId[i];    /*Set the channel id for this entry*/
            }
            fpi->edch_type = fp_edch_channel_info->edch_type;

            rlcinf->ueid[0] = get_ue_id_from_conv(p_conv_data);
            rlcinf->li_size[0] = RLC_LI_7BITS;
            rlcinf->ciphered[0] = FALSE;
            rlcinf->deciphered[0] = FALSE;

            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0, macinf);
            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0, rlcinf);

            return fpi;

        case CHANNEL_PCH:
            fp_pch_channel_info = (fp_pch_channel_info_t*)p_conv_data->channel_specific_info;
            if(fp_pch_channel_info == NULL) {
                proto_tree_add_expert_format(tree, pinfo, &ei_fp_no_per_conv_channel_info, tvb, offset, -1,
                                      "Can't dissect PCH FP stream because no per-conversation channel info was attached!");
                info_missing = TRUE;
                break;
            }
            fpi->paging_indications = fp_pch_channel_info->paging_indications;
            fpi->num_chans = p_conv_data->num_dch_in_flow;

            if (is_control_frame) {
                /* control frame, we're done */
                return fpi;
            }
            /* Inesrting Paging Indication Info extracted from the previous packet */
            fpi->relevant_paging_indications = fp_pch_channel_info->last_paging_indication_info;
            fp_pch_channel_info->last_paging_indication_info = NULL;

            /* Set offset to TFI */
            offset = 3;
            break;
        case CHANNEL_DCH:
            fpi->num_chans = p_conv_data->num_dch_in_flow;
            if (is_control_frame) {
                /* control frame, we're done */
                return fpi;
            }

            rlcinf = wmem_new0(wmem_file_scope(), rlc_info);
            macinf = wmem_new0(wmem_file_scope(), umts_mac_info);
            offset = 2; /* Set offset to TFI */
            fakes  = 5; /* Reset fake counter */
            for (chan=0; chan < fpi->num_chans; chan++) { /* Iterate over the DCH channels in the flow (each given a TFI) */
                /* TFI is 5 bits according to 3GPP TS 25.427, paragraph 6.2.4.4 */
                tfi = tvb_get_bits8(tvb, 3+offset*8, 5);

                /* Figure out the number of TBs and size */
                num_tbs = (fpi->is_uplink) ?
                    p_conv_data->fp_dch_channel_info[chan].ul_chan_num_tbs[tfi] :
                    p_conv_data->fp_dch_channel_info[chan].dl_chan_num_tbs[tfi];
                tb_size = (fpi->is_uplink) ?
                    p_conv_data->fp_dch_channel_info[chan].ul_chan_tf_size[tfi] :
                    p_conv_data->fp_dch_channel_info[chan].dl_chan_tf_size[tfi];

                tb_bit_off = (2 + p_conv_data->num_dch_in_flow) * 8; /*Point to the C/T of first TB*/
                /* Iterate over the Transport Blocks */
                /* Set configuration for each individual block */
                for (j=0; j < num_tbs && j+chan < MAX_MAC_FRAMES; j++) {
                    /* Set transport channel id (useful for debugging) */
                    macinf->trchid[j+chan] = p_conv_data->dch_ids_in_flow_list[chan];

                    /* Checking for the common Transport Format of 3.4 kbps SRBs for DCCH ( See 3GPP TR 25.944 / 4.1.1.3.1.1 ) */
                    is_known_dcch_tf = (tfi == 1 && num_tbs == 1 && tb_size == 148);
                    /* Checking for Transport Format of interactive or background PS RAB ( See 3GPP TS 34.108 / 6.10.2.4.1.23 -> 6.10.2.4.1.35 ) */
                    is_stndalone_ps_rab_tf = tb_size == 336;
                    /* Checking for Transport Format of muxed CS & PS RABs ( See 3GPP TS 34.108 / 6.10.2.4.1.38 -> 6.10.2.4.1.51 ) */
                    is_muxed_cs_ps_tf = (p_conv_data->dch_ids_in_flow_list[chan] == 24 && tb_size == 340);

                    if (is_known_dcch_tf || is_muxed_cs_ps_tf) {
                        /* Channel is multiplexed (ie. C/T field present) */
                        macinf->ctmux[j+chan] = TRUE;

                        /* Peek at C/T, different RLC params for different logical channels */
                        /* C/T is 4 bits according to 3GPP TS 25.321, paragraph 9.2.1, from MAC header (not FP) */
                        c_t = tvb_get_bits8(tvb, tb_bit_off, 4);
                        lchid = (c_t + 1) % 0xf; /* C/T field represents the Logical Channel ID but it is zero-based */
                        macinf->lchid[j+chan] = lchid;
                        macinf->content[j+chan] = lchId_type_table[lchid]; /* Base MAC content on logical channel id (Table is in packet-nbap.h) */
                        rlcinf->mode[j+chan] = lchId_rlc_map[lchid];       /* Base RLC mode on logical channel id */
                    }
                    else if (is_stndalone_ps_rab_tf) {
                        /* Channel isn't multiplexed (ie. C/T field not present) */
                        macinf->ctmux[j+chan] = FALSE;

                        /* Using a fake 'interactive PS' DTCH logical channel id */
                        /* TODO: Once proper lchid is always set, this has to be changed */
                        macinf->fake_chid[j+chan] = TRUE;
                        macinf->lchid[j+chan] = 11;
                        macinf->content[j+chan] = MAC_CONTENT_PS_DTCH;
                        rlcinf->mode[j+chan] = RLC_AM;
                    }
                    else {
                        /* Unfamiliar DCH format, faking LCHID */
                        /* Asuming the channel isn't multiplexed (ie. C/T field not present) */
                        macinf->ctmux[j+chan] = FALSE;

                        /* TODO: This stuff has to be reworked! */
                        /* Generates a fake logical channel id for non multiplexed channel */
                        fake_lchid = make_fake_lchid(pinfo, p_conv_data->dch_ids_in_flow_list[chan]);
                        macinf->content[j+chan] = lchId_type_table[fake_lchid];
                        rlcinf->mode[j+chan] = lchId_rlc_map[fake_lchid];

                        /************************/
                        /* TODO: Once proper lchid is always set, this has to be removed */
                        macinf->fake_chid[j+chan] = TRUE;
                        macinf->lchid[j+chan] = fake_lchid;
                        /************************/
                    }

                    /* Set RLC data */
                    rlcinf->ueid[j + chan] = get_ue_id_from_conv(p_conv_data);
                    rlcinf->li_size[j+chan] = RLC_LI_7BITS;
                    rlcinf->ciphered[j+chan] = FALSE;
                    rlcinf->deciphered[j+chan] = FALSE;
                    rlcinf->rbid[j+chan] = macinf->lchid[j+chan];

                    /*Step over this TB and it's C/T field.*/
                    tb_bit_off += tb_size+4;
                }

                offset++;
            }
            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0, macinf);
            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0, rlcinf);
            /* Set offset to point to first TFI */
            offset = 2;
            break;
        case CHANNEL_FACH_FDD:
            fp_fach_channel_info = (fp_fach_channel_info_t*)p_conv_data->channel_specific_info;
            if(fp_fach_channel_info == NULL) {
                proto_tree_add_expert_format(tree, pinfo, &ei_fp_no_per_conv_channel_info, tvb, offset, -1,
                                      "Can't dissect FACH FP stream because no per-conversation channel info was attached!");
                info_missing = TRUE;
                break;
            }
            fpi->num_chans = p_conv_data->num_dch_in_flow;
            if (is_control_frame) {
                /* control frame, we're done */
                return fpi;
            }
            /* Set offset to TFI */
            offset = 2;
            /* Set MAC data */
            macinf = wmem_new0(wmem_file_scope(), umts_mac_info);
            macinf->ctmux[0]   = 1;
            macinf->content[0] = MAC_CONTENT_DCCH;
            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0, macinf);
            /* Set RLC data */
            rlcinf = wmem_new0(wmem_file_scope(), rlc_info);
            /* For RLC reassembly to work we need to fake a "UE ID" as an identifier of this stream.*/
            /* Using the (UDP) conversation's ID and the prefix of 0xFFF */
            rlcinf->ueid[0] = (p_conv->conv_index | 0xFFF00000);
            rlcinf->mode[0] = RLC_AM;
            rlcinf->li_size[0] = RLC_LI_7BITS;
            rlcinf->ciphered[0] = FALSE;
            rlcinf->deciphered[0] = FALSE;
            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0, rlcinf);
            break;

        case CHANNEL_RACH_FDD:
            fp_rach_channel_info = (fp_rach_channel_info_t*)p_conv_data->channel_specific_info;
            if(fp_rach_channel_info == NULL) {
                proto_tree_add_expert_format(tree, pinfo, &ei_fp_no_per_conv_channel_info, tvb, offset, -1,
                                      "Can't dissect RACH FP stream because no per-conversation channel info was attached!");
                info_missing = TRUE;
                break;
            }
            fpi->num_chans = p_conv_data->num_dch_in_flow;
            if (is_control_frame) {
                /* control frame, we're done */
                return fpi;
            }
            /* Set offset to TFI */
            offset = 2;
            /* set MAC & RLC data */
            macinf = wmem_new0(wmem_file_scope(), umts_mac_info);
            rlcinf = wmem_new0(wmem_file_scope(), rlc_info);
            for ( chan = 0; chan < fpi->num_chans; chan++ ) {
                macinf->ctmux[chan]   = 1;
                macinf->content[chan] = MAC_CONTENT_DCCH;
                /* RLC dissector's reassembly requires a non-zero stream identifier ('UE ID') to work */
                /* For DCCH: MAC dissector will override this with C-RNTI/U-RNTI */
                /* For CCCH: RLC's mode is TM and the dissector does not reassemble at all - showing 0 in the UI to indicate that */
                rlcinf->ueid[chan] = 0;
            }
            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0, macinf);
            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0, rlcinf);
            break;
        case CHANNEL_HSDSCH_COMMON:
            rlcinf = wmem_new0(wmem_file_scope(), rlc_info);
            macinf = wmem_new0(wmem_file_scope(), umts_mac_info);
            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0, macinf);
            p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0, rlcinf);
            break;
        default:
            expert_add_info(pinfo, NULL, &ei_fp_transport_channel_type_unknown);
            info_missing = TRUE;
            break;
    }

    if(info_missing) {
        /* Some information was missing in the conversation struct and the FP info isn't complete */
        p_remove_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);
        wmem_free(wmem_file_scope(), fpi);
        return NULL;
    }

    /* Peek at the packet as the per packet info seems not to take the tfi into account */
    for (i=0; i<fpi->num_chans; i++) {
        /*TFI is 5 bits according to 3GPP TS 25.427, paragraph 6.2.4.4*/
        tfi = tvb_get_guint8(tvb, offset) & 0x1f;
        if (pinfo->link_dir == P2P_DIR_UL) {
            fpi->chan_tf_size[i] = p_conv_data->fp_dch_channel_info[i].ul_chan_tf_size[tfi];
            fpi->chan_num_tbs[i] = p_conv_data->fp_dch_channel_info[i].ul_chan_num_tbs[tfi];
        } else {
            fpi->chan_tf_size[i] = p_conv_data->fp_dch_channel_info[i].dl_chan_tf_size[tfi];
            fpi->chan_num_tbs[i] = p_conv_data->fp_dch_channel_info[i].dl_chan_num_tbs[tfi];
        }
        offset++;
    }


    return fpi;
}

/* Updates the conversation info of a PCH stream based on information parsed in the current frame*/
static void
update_pch_coversation_info(umts_fp_conversation_info_t *p_conv_data, packet_info *pinfo, struct fp_info *p_fp_info)
{
    fp_pch_channel_info_t* fp_pch_channel_info;
    /* The channel type MUST be set to PCH */
    DISSECTOR_ASSERT(p_conv_data);
    DISSECTOR_ASSERT(p_conv_data->channel == CHANNEL_PCH);

    fp_pch_channel_info = (fp_pch_channel_info_t*)p_conv_data->channel_specific_info;
    if(p_fp_info->current_paging_indications && !PINFO_FD_VISITED(pinfo))
    {
        /* Saving the PI info for the next packet to find */
        fp_pch_channel_info->last_paging_indication_info = p_fp_info->current_paging_indications;
        /* Resetting this field so we don't add it again to the conversation next time the packet is parsed */
        p_fp_info->current_paging_indications = NULL;
    }
}

/*****************************/
/* Main dissection function. */
static int
dissect_fp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
    proto_tree       *fp_tree;
    proto_item       *ti;
    gint              offset = 0;
    struct fp_info   *p_fp_info;
    conversation_t   *p_conv = NULL;
    umts_fp_conversation_info_t *p_conv_data = NULL;

    /* Append this protocol name rather than replace. */
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "FP");

    /* Create fp tree. */
    ti = proto_tree_add_item(tree, proto_fp, tvb, offset, -1, ENC_NA);
    fp_tree = proto_item_add_subtree(ti, ett_fp);

    top_level_tree = tree;

    /* Look for packet info! */
    p_fp_info = (struct fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);

    /* Check if we have conversation info */
    /* Trying to find exact match - with both RNC's address & port and Node B's address & port */
    p_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
                               conversation_pt_to_endpoint_type(pinfo->ptype),
                               pinfo->destport, pinfo->srcport, 0);
    if (p_conv) {
        p_conv_data = (umts_fp_conversation_info_t *)conversation_get_proto_data(p_conv, proto_fp);
    }
    if (!p_conv || !p_conv_data) {
        /* Didn't find exact conversation match */
        /* Try to find a partial match with just the source/destination included */
        p_conv = (conversation_t *)find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
                                   conversation_pt_to_endpoint_type(pinfo->ptype),
                                   pinfo->destport, pinfo->srcport, NO_ADDR2);
        if (p_conv) {
            p_conv_data = (umts_fp_conversation_info_t *)conversation_get_proto_data(p_conv, proto_fp);
        }
    }

    if (p_conv_data) {
        /*Figure out the direction of the link*/
        if (addresses_equal(&(pinfo->net_dst), (&p_conv_data->crnc_address))) {
            /* Node B -> CRNC*/
            pinfo->link_dir=P2P_DIR_UL;

            proto_item *item= proto_tree_add_uint(fp_tree, hf_fp_ul_setup_frame,
                                                  tvb, 0, 0, p_conv_data->ul_frame_number);
            proto_item_set_generated(item);
        }
        else {
            /* CRNC -> Node B */
            pinfo->link_dir=P2P_DIR_DL;

            /* Maybe the frame number should be stored in the proper location already in nbap?, in ul_frame_number*/
            proto_item *item= proto_tree_add_uint(fp_tree, hf_fp_dl_setup_frame,
                                                   tvb, 0, 0, p_conv_data->ul_frame_number);
            proto_item_set_generated(item);
        }
        if (p_fp_info == NULL) {
            p_fp_info = fp_set_per_packet_inf_from_conv(p_conv, p_conv_data, tvb, pinfo, fp_tree);
        }
    }

    if (pinfo->p2p_dir == P2P_DIR_UNKNOWN) {
        if (pinfo->link_dir == P2P_DIR_UL) {
            pinfo->p2p_dir = P2P_DIR_RECV;
        } else {
            pinfo->p2p_dir = P2P_DIR_SENT;
        }
    }

    /* Can't dissect anything without it... */
    if (p_fp_info == NULL) {
        proto_tree_add_expert(fp_tree, pinfo, &ei_fp_no_per_frame_info, tvb, offset, -1);
        return 1;
    }

    /* Show release information */
    if (preferences_show_release_info) {
        proto_item *release_ti;
        proto_tree *release_tree;
        proto_item *temp_ti;

        release_ti = proto_tree_add_item(fp_tree, hf_fp_release, tvb, 0, 0, ENC_NA);
        proto_item_set_generated(release_ti);
        proto_item_append_text(release_ti, " R%u (%d/%d)",
                               p_fp_info->release, p_fp_info->release_year, p_fp_info->release_month);
        release_tree = proto_item_add_subtree(release_ti, ett_fp_release);

        temp_ti = proto_tree_add_uint(release_tree, hf_fp_release_version, tvb, 0, 0, p_fp_info->release);
        proto_item_set_generated(temp_ti);

        temp_ti = proto_tree_add_uint(release_tree, hf_fp_release_year, tvb, 0, 0, p_fp_info->release_year);
        proto_item_set_generated(temp_ti);

        temp_ti = proto_tree_add_uint(release_tree, hf_fp_release_month, tvb, 0, 0, p_fp_info->release_month);
        proto_item_set_generated(temp_ti);
    }

    /* Show channel type in info column, tree */
    col_set_str(pinfo->cinfo, COL_INFO,
                val_to_str_const(p_fp_info->channel,
                                 channel_type_vals,
                                 "Unknown channel type"));
    if (p_conv_data) {
        int i;
        col_append_fstr(pinfo->cinfo, COL_INFO, "(%u", p_conv_data->dch_ids_in_flow_list[0]);
        for (i=1; i < p_conv_data->num_dch_in_flow; i++) {
            col_append_fstr(pinfo->cinfo, COL_INFO, ",%u", p_conv_data->dch_ids_in_flow_list[i]);
        }
        col_append_fstr(pinfo->cinfo, COL_INFO, ") ");
    }
    proto_item_append_text(ti, " (%s)",
                           val_to_str_const(p_fp_info->channel,
                                            channel_type_vals,
                                            "Unknown channel type"));

    /* Add channel type as a generated field */
    ti = proto_tree_add_uint(fp_tree, hf_fp_channel_type, tvb, 0, 0, p_fp_info->channel);
    proto_item_set_generated(ti);

    /* Add division type as a generated field */
    if (p_fp_info->release == 7) {
        ti = proto_tree_add_uint(fp_tree, hf_fp_division, tvb, 0, 0, p_fp_info->division);
        proto_item_set_generated(ti);
    }

    /* Add link direction as a generated field */
    ti = proto_tree_add_uint(fp_tree, hf_fp_direction, tvb, 0, 0, p_fp_info->is_uplink);
    proto_item_set_generated(ti);

    /* Don't currently handle IuR-specific formats, but it's useful to even see
       the channel type and direction */
    if (p_fp_info->iface_type == IuR_Interface) {
        return 1;
    }

    /* Show DDI config info */
    if (p_fp_info->no_ddi_entries > 0) {
        int n;
        proto_item *ddi_config_ti;
        proto_tree *ddi_config_tree;

        ddi_config_ti = proto_tree_add_string_format(fp_tree, hf_fp_ddi_config, tvb, offset, 0,
                                                     "", "DDI Config (");
        proto_item_set_generated(ddi_config_ti);
        ddi_config_tree = proto_item_add_subtree(ddi_config_ti, ett_fp_ddi_config);

        /* Add each entry */
        for (n=0; n < p_fp_info->no_ddi_entries; n++) {
            proto_item_append_text(ddi_config_ti, "%s%u->%ubits",
                                   (n == 0) ? "" : "  ",
                                   p_fp_info->edch_ddi[n], p_fp_info->edch_macd_pdu_size[n]);
            ti = proto_tree_add_uint(ddi_config_tree, hf_fp_ddi_config_ddi, tvb, 0, 0,
                                p_fp_info->edch_ddi[n]);
            proto_item_set_generated(ti);
            ti = proto_tree_add_uint(ddi_config_tree, hf_fp_ddi_config_macd_pdu_size, tvb, 0, 0,
                                p_fp_info->edch_macd_pdu_size[n]);
            proto_item_set_generated(ti);

        }
        proto_item_append_text(ddi_config_ti, ")");
    }

    /*************************************/
    /* Dissect according to channel type */
    switch (p_fp_info->channel) {
        case CHANNEL_RACH_TDD:
        case CHANNEL_RACH_TDD_128:
        case CHANNEL_RACH_FDD:
            dissect_rach_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info,
                                      data);
            break;
        case CHANNEL_DCH:
            dissect_dch_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info,
                                     data);
            break;
        case CHANNEL_FACH_FDD:
        case CHANNEL_FACH_TDD:
            dissect_fach_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info,
                                      data);
            break;
        case CHANNEL_DSCH_FDD:
        case CHANNEL_DSCH_TDD:
            dissect_dsch_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info);
            break;
        case CHANNEL_USCH_TDD_128:
        case CHANNEL_USCH_TDD_384:
            dissect_usch_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info);
            break;
        case CHANNEL_PCH:
            dissect_pch_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info,
                                     data);
            update_pch_coversation_info(p_conv_data, pinfo, p_fp_info);
            break;
        case CHANNEL_CPCH:
            dissect_cpch_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info);
            break;
        case CHANNEL_BCH:
            dissect_bch_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info);
            break;
        case CHANNEL_HSDSCH:
            /* Show configured MAC HS-DSCH entity in use */
            if (fp_tree)
            {
                proto_item *entity_ti;
                entity_ti = proto_tree_add_uint(fp_tree, hf_fp_hsdsch_entity,
                                                tvb, 0, 0,
                                                p_fp_info->hsdsch_entity);
                proto_item_set_generated(entity_ti);
            }
            switch (p_fp_info->hsdsch_entity) {
                case entity_not_specified:
                case hs:
                    /* This is the pre-R7 default */
                    dissect_hsdsch_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info, data);
                    break;
                case ehs:
                    dissect_hsdsch_type_2_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info, data);
                    break;
                default:
                    /* Report Error */
                    expert_add_info(pinfo, NULL, &ei_fp_hsdsch_entity_not_specified);
                    break;
            }
            break;
        case CHANNEL_HSDSCH_COMMON:
            expert_add_info(pinfo, NULL, &ei_fp_hsdsch_common_experimental_support);
            /*if (FALSE)*/
            dissect_hsdsch_common_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info, data);

            break;
        case CHANNEL_HSDSCH_COMMON_T3:
            expert_add_info(pinfo, NULL, &ei_fp_hsdsch_common_t3_not_implemented);

            /* TODO: */
            break;
        case CHANNEL_IUR_CPCHF:
            /* TODO: */
            break;
        case CHANNEL_IUR_FACH:
            /* TODO: */
            break;
        case CHANNEL_IUR_DSCH:
            dissect_iur_dsch_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info);
            break;
        case CHANNEL_EDCH:
        case CHANNEL_EDCH_COMMON:
            /* Show configured MAC E-DCH entity in use */
            if (fp_tree)
            {
                proto_item *entity_ti;
                entity_ti = proto_tree_add_uint(fp_tree, hf_fp_edch_entity,
                                                tvb, 0, 0,
                                                p_fp_info->edch_type);
                proto_item_set_generated(entity_ti);
            }
            dissect_e_dch_channel_info(tvb, pinfo, fp_tree, offset, p_fp_info,
                                       p_fp_info->channel == CHANNEL_EDCH_COMMON,
                                       data);
            break;

        default:
            expert_add_info(pinfo, NULL, &ei_fp_channel_type_unknown);
            break;
    }
    return tvb_captured_length(tvb);
}

static int
dissect_fp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    return dissect_fp_common(tvb, pinfo, tree, NULL);
}

static int
dissect_fp_aal2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
    return dissect_fp_common(tvb, pinfo, tree, data);
}

void proto_register_fp(void)
{
    static hf_register_info hf[] =
        {
            { &hf_fp_release,
              { "Release",
                "fp.release", FT_NONE, BASE_NONE, NULL, 0x0,
                "Release information", HFILL
              }
            },
            { &hf_fp_release_version,
              { "Release Version",
                "fp.release.version", FT_UINT8, BASE_DEC, NULL, 0x0,
                "3GPP Release number", HFILL
              }
            },
            { &hf_fp_release_year,
              { "Release year",
                "fp.release.year", FT_UINT16, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_release_month,
              { "Release month",
                "fp.release.month", FT_UINT8, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_channel_type,
              { "Channel Type",
                "fp.channel-type", FT_UINT8, BASE_HEX, VALS(channel_type_vals), 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_division,
              { "Division",
                "fp.division", FT_UINT8, BASE_HEX, VALS(division_vals), 0x0,
                "Radio division type", HFILL
              }
            },
            { &hf_fp_direction,
              { "Direction",
                "fp.direction", FT_UINT8, BASE_HEX, VALS(direction_vals), 0x0,
                "Link direction", HFILL
              }
            },
            { &hf_fp_ddi_config,
              { "DDI Config",
                "fp.ddi-config", FT_STRING, BASE_NONE, NULL, 0x0,
                "DDI Config (for E-DCH)", HFILL
              }
            },
            { &hf_fp_ddi_config_ddi,
              { "DDI",
                "fp.ddi-config.ddi", FT_UINT8, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_ddi_config_macd_pdu_size,
              { "MACd PDU Size",
                "fp.ddi-config.macd-pdu-size", FT_UINT16, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            },


            { &hf_fp_header_crc,
              { "Header CRC",
                "fp.header-crc", FT_UINT8, BASE_HEX, NULL, 0xfe,
                NULL, HFILL
              }
            },
            { &hf_fp_ft,
              { "Frame Type",
                "fp.ft", FT_UINT8, BASE_HEX, VALS(data_control_vals), 0x01,
                NULL, HFILL
              }
            },
            { &hf_fp_cfn,
              { "CFN",
                "fp.cfn", FT_UINT8, BASE_DEC, NULL, 0xff,
                "Connection Frame Number", HFILL
              }
            },
            { &hf_fp_pch_cfn,
              { "CFN (PCH)",
                "fp.pch.cfn", FT_UINT16, BASE_DEC, NULL, 0xfff0,
                "PCH Connection Frame Number", HFILL
              }
            },
            { &hf_fp_pch_toa,
              { "ToA (PCH)",
                "fp.pch.toa", FT_INT24, BASE_DEC, NULL, 0x0,
                "PCH Time of Arrival", HFILL
              }
            },
            { &hf_fp_cfn_control,
              { "CFN control",
                "fp.cfn-control", FT_UINT8, BASE_DEC, NULL, 0xff,
                "Connection Frame Number Control", HFILL
              }
            },
            { &hf_fp_toa,
              { "ToA",
                "fp.toa", FT_INT16, BASE_DEC, NULL, 0x0,
                "Time of arrival (units are 125 microseconds)", HFILL
              }
            },
            { &hf_fp_tb,
              { "TB",
                "fp.tb", FT_BYTES, BASE_NONE, NULL, 0x0,
                "Transport Block", HFILL
              }
            },
            { &hf_fp_chan_zero_tbs,
              { "No TBs for channel",
                "fp.channel-with-zero-tbs", FT_UINT32, BASE_DEC, NULL, 0x0,
                "Channel with 0 TBs", HFILL
              }
            },
            { &hf_fp_tfi,
              { "TFI",
                "fp.tfi", FT_UINT8, BASE_DEC, NULL, 0x0,
                "Transport Format Indicator", HFILL
              }
            },
            { &hf_fp_usch_tfi,
              { "TFI",
                "fp.usch.tfi", FT_UINT8, BASE_DEC, NULL, 0x1f,
                "USCH Transport Format Indicator", HFILL
              }
            },
            { &hf_fp_cpch_tfi,
              { "TFI",
                "fp.cpch.tfi", FT_UINT8, BASE_DEC, NULL, 0x1f,
                "CPCH Transport Format Indicator", HFILL
              }
            },
            { &hf_fp_propagation_delay,
              { "Propagation Delay",
                "fp.propagation-delay", FT_UINT8, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_dch_control_frame_type,
              { "Control Frame Type",
                "fp.dch.control.frame-type", FT_UINT8, BASE_HEX, VALS(dch_control_frame_type_vals), 0x0,
                "DCH Control Frame Type", HFILL
              }
            },
            { &hf_fp_dch_rx_timing_deviation,
              { "Rx Timing Deviation",
                "fp.dch.control.rx-timing-deviation", FT_UINT8, BASE_DEC, 0, 0x0,
                "DCH Rx Timing Deviation", HFILL
              }
            },
            { &hf_fp_quality_estimate,
              { "Quality Estimate",
                "fp.dch.quality-estimate", FT_UINT8, BASE_DEC, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_payload_crc,
              { "Payload CRC",
                "fp.payload-crc", FT_UINT16, BASE_HEX, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_common_control_frame_type,
              { "Control Frame Type",
                "fp.common.control.frame-type", FT_UINT8, BASE_HEX, VALS(common_control_frame_type_vals), 0x0,
                "Common Control Frame Type", HFILL
              }
            },
            { &hf_fp_crci[0],
              { "CRCI",
                "fp.crci", FT_UINT8, BASE_HEX, VALS(crci_vals), 0x80,
                "CRC correctness indicator", HFILL
              }
            },
            { &hf_fp_crci[1],
              { "CRCI",
                "fp.crci", FT_UINT8, BASE_HEX, VALS(crci_vals), 0x40,
                "CRC correctness indicator", HFILL
              }
            },
            { &hf_fp_crci[2],
              { "CRCI",
                "fp.crci", FT_UINT8, BASE_HEX, VALS(crci_vals), 0x20,
                "CRC correctness indicator", HFILL
              }
            },
            { &hf_fp_crci[3],
              { "CRCI",
                "fp.crci", FT_UINT8, BASE_HEX, VALS(crci_vals), 0x10,
                "CRC correctness indicator", HFILL
              }
            },
            { &hf_fp_crci[4],
              { "CRCI",
                "fp.crci", FT_UINT8, BASE_HEX, VALS(crci_vals), 0x08,
                "CRC correctness indicator", HFILL
              }
            },
            { &hf_fp_crci[5],
              { "CRCI",
                "fp.crci", FT_UINT8, BASE_HEX, VALS(crci_vals), 0x04,
                "CRC correctness indicator", HFILL
              }
            },
            { &hf_fp_crci[6],
              { "CRCI",
                "fp.crci", FT_UINT8, BASE_HEX, VALS(crci_vals), 0x02,
                "CRC correctness indicator", HFILL
              }
            },
            { &hf_fp_crci[7],
              { "CRCI",
                "fp.crci", FT_UINT8, BASE_HEX, VALS(crci_vals), 0x01,
                "CRC correctness indicator", HFILL
              }
            },
            { &hf_fp_received_sync_ul_timing_deviation,
              { "Received SYNC UL Timing Deviation",
                "fp.rx-sync-ul-timing-deviation", FT_UINT8, BASE_DEC, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_pch_pi,
              { "Paging Indication",
                "fp.pch.pi", FT_UINT8, BASE_DEC, VALS(paging_indication_vals), 0x01,
                "Indicates if the PI Bitmap is present", HFILL
              }
            },
            { &hf_fp_pch_tfi,
              { "TFI",
                "fp.pch.tfi", FT_UINT8, BASE_DEC, 0, 0x1f,
                "PCH Transport Format Indicator", HFILL
              }
            },
            { &hf_fp_fach_tfi,
              { "TFI",
                "fp.fach.tfi", FT_UINT8, BASE_DEC, 0, 0x1f,
                "FACH Transport Format Indicator", HFILL
              }
            },
            { &hf_fp_transmit_power_level,
              { "Transmit Power Level",
                "fp.transmit-power-level", FT_FLOAT, BASE_NONE, 0, 0x0,
                "Transmit Power Level (dB)", HFILL
              }
            },
            { &hf_fp_pdsch_set_id,
              { "PDSCH Set Id",
                "fp.pdsch-set-id", FT_UINT8, BASE_DEC, 0, 0x0,
                "A pointer to the PDSCH Set which shall be used to transmit", HFILL
              }
            },
            { &hf_fp_paging_indication_bitmap,
              { "Paging Indications bitmap",
                "fp.pch.pi-bitmap", FT_BYTES , BASE_NONE, NULL, 0x0,
                "Paging Indication bitmap", HFILL
              }
            },
            { &hf_fp_relevant_paging_indication_bitmap,
              { "Relevant Paging Indications bitmap",
                "fp.pch.relevant-pi-bitmap", FT_BYTES , BASE_NONE, NULL, 0x0,
                "The Paging Indication bitmap used to inform users about the current frame", HFILL
              }
            },
            { &hf_fp_rx_timing_deviation,
              { "Rx Timing Deviation",
                "fp.common.control.rx-timing-deviation", FT_UINT8, BASE_DEC, 0, 0x0,
                "Common Rx Timing Deviation", HFILL
              }
            },
            { &hf_fp_dch_e_rucch_flag,
              { "E-RUCCH Flag",
                "fp.common.control.e-rucch-flag", FT_UINT8, BASE_DEC, VALS(e_rucch_flag_vals), 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_edch_header_crc,
              { "E-DCH Header CRC",
                "fp.edch.header-crc", FT_UINT16, BASE_HEX, 0, 0,
                NULL, HFILL
              }
            },
            { &hf_fp_edch_fsn,
              { "FSN",
                "fp.edch.fsn", FT_UINT8, BASE_DEC, 0, 0x0f,
                "E-DCH Frame Sequence Number", HFILL
              }
            },
            { &hf_fp_edch_number_of_subframes,
              { "No of subframes",
                "fp.edch.no-of-subframes", FT_UINT8, BASE_DEC, 0, 0x0f,
                "E-DCH Number of subframes", HFILL
              }
            },
            { &hf_fp_edch_harq_retransmissions,
              { "No of HARQ Retransmissions",
                "fp.edch.no-of-harq-retransmissions", FT_UINT8, BASE_DEC, 0, 0x78,
                "E-DCH Number of HARQ retransmissions", HFILL
              }
            },
            { &hf_fp_edch_subframe_number,
              { "Subframe number",
                "fp.edch.subframe-number", FT_UINT8, BASE_DEC, 0, 0x0,
                "E-DCH Subframe number", HFILL
              }
            },
            { &hf_fp_edch_number_of_mac_es_pdus,
              { "Number of Mac-es PDUs",
                "fp.edch.number-of-mac-es-pdus", FT_UINT8, BASE_DEC, 0, 0xf0,
                NULL, HFILL
              }
            },
            { &hf_fp_edch_ddi,
              { "DDI",
                "fp.edch.ddi", FT_UINT8, BASE_DEC, 0, 0x0,
                "E-DCH Data Description Indicator", HFILL
              }
            },
            { &hf_fp_edch_subframe,
              { "Subframe",
                "fp.edch.subframe", FT_STRING, BASE_NONE, NULL, 0x0,
                "EDCH Subframe", HFILL
              }
            },
            { &hf_fp_edch_subframe_header,
              { "Subframe header",
                "fp.edch.subframe-header", FT_STRING, BASE_NONE, NULL, 0x0,
                "EDCH Subframe header", HFILL
              }
            },
            { &hf_fp_edch_number_of_mac_d_pdus,
              { "Number of Mac-d PDUs",
                "fp.edch.number-of-mac-d-pdus", FT_UINT8, BASE_DEC, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_edch_pdu_padding,
              { "Padding",
                "fp.edch-data-padding", FT_UINT8, BASE_DEC, 0, 0xc0,
                "E-DCH padding before PDU", HFILL
              }
            },
            { &hf_fp_edch_tsn,
              { "TSN",
                "fp.edch-tsn", FT_UINT8, BASE_DEC, 0, 0x3f,
                "E-DCH Transmission Sequence Number", HFILL
              }
            },
            { &hf_fp_edch_mac_es_pdu,
              { "MAC-es PDU",
                "fp.edch.mac-es-pdu", FT_NONE, BASE_NONE, NULL, 0x0,
                NULL, HFILL
              }
            },

            { &hf_fp_edch_user_buffer_size,
              { "User Buffer Size",
                "fp.edch.user-buffer-size", FT_UINT24, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_edch_no_macid_sdus,
              { "No of MAC-is SDUs",
                "fp.edch.no-macis-sdus", FT_UINT16, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_edch_number_of_mac_is_pdus,
              { "Number of Mac-is PDUs",
                "fp.edch.number-of-mac-is-pdus", FT_UINT8, BASE_DEC, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_edch_mac_is_pdu,
              { "Mac-is PDU",
                "fp.edch.mac-is-pdu", FT_BYTES, BASE_NONE, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_edch_e_rnti,
              { "E-RNTI",
                "fp.edch.e-rnti", FT_UINT16, BASE_DEC, 0, 0x0,
                NULL, HFILL
              }
            },

            { &hf_fp_edch_macis_descriptors,
              { "MAC-is Descriptors",
                "fp.edch.mac-is.descriptors", FT_STRING, BASE_NONE, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_edch_macis_lchid,
              { "LCH-ID",
                "fp.edch.mac-is.lchid", FT_UINT8, BASE_HEX, VALS(lchid_vals), 0xf0,
                NULL, HFILL
              }
            },
            { &hf_fp_edch_macis_length,
              { "Length",
                "fp.edch.mac-is.length", FT_UINT16, BASE_DEC, 0, 0x0ffe,
                NULL, HFILL
              }
            },
            { &hf_fp_edch_macis_flag,
              { "Flag",
                "fp.edch.mac-is.lchid", FT_UINT8, BASE_HEX, 0, 0x01,
                "Indicates if another entry follows", HFILL
              }
            },
            { &hf_fp_edch_entity,
              { "E-DCH Entity",
                "fp.edch.entity", FT_UINT8, BASE_DEC, VALS(edch_mac_entity_vals), 0x0,
                "Type of MAC entity for this E-DCH channel", HFILL
              }
            },
            { &hf_fp_frame_seq_nr,
              { "Frame Seq Nr",
                "fp.frame-seq-nr", FT_UINT8, BASE_DEC, 0, 0xf0,
                "Frame Sequence Number", HFILL
              }
            },
            { &hf_fp_hsdsch_pdu_block_header,
              { "PDU block header",
                "fp.hsdsch.pdu-block-header", FT_STRING, BASE_NONE, NULL, 0x0,
                "HS-DSCH type 2 PDU block header", HFILL
              }
            },
#if 0
            { &hf_fp_hsdsch_pdu_block,
              { "PDU block",
                "fp.hsdsch.pdu-block", FT_STRING, BASE_NONE, NULL, 0x0,
                "HS-DSCH type 2 PDU block data", HFILL
              }
            },
#endif
            { &hf_fp_flush,
              { "Flush",
                "fp.flush", FT_UINT8, BASE_DEC, 0, 0x04,
                "Whether all PDUs for this priority queue should be removed", HFILL
              }
            },
            { &hf_fp_fsn_drt_reset,
              { "FSN-DRT reset",
                "fp.fsn-drt-reset", FT_UINT8, BASE_DEC, 0, 0x02,
                "FSN/DRT Reset Flag", HFILL
              }
            },
            { &hf_fp_drt_indicator,
              { "DRT Indicator",
                "fp.drt-indicator", FT_UINT8, BASE_DEC, 0, 0x01,
                NULL, HFILL
              }
            },
            { &hf_fp_fach_indicator,
              { "FACH Indicator",
                "fp.fach-indicator", FT_UINT8, BASE_DEC, 0, 0x80,
                NULL, HFILL
              }
            },
            { &hf_fp_total_pdu_blocks,
              { "PDU Blocks",
                "fp.pdu_blocks", FT_UINT8, BASE_DEC, 0, 0xf8,
                "Total number of PDU blocks", HFILL
              }
            },
            { &hf_fp_drt,
              { "DelayRefTime",
                "fp.drt", FT_UINT16, BASE_DEC, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_hrnti,
              { "HRNTI",
                "fp.hrnti", FT_UINT16, BASE_DEC, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_rach_measurement_result,
              { "RACH Measurement Result",
                "fp.rach-measurement-result", FT_UINT16, BASE_DEC, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_lchid,
              { "Logical Channel ID",
                "fp.lchid", FT_UINT8, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_pdu_length_in_block,
              { "PDU length in block",
                "fp.pdu-length-in-block", FT_UINT8, BASE_DEC, 0, 0x0,
                "Length of each PDU in this block in bytes", HFILL
              }
            },
            { &hf_fp_pdus_in_block,
              { "PDUs in block",
                "fp.no-pdus-in-block", FT_UINT8, BASE_DEC, 0, 0x0,
                "Number of PDUs in block", HFILL
              }
            },
            { &hf_fp_cmch_pi,
              { "CmCH-PI",
                "fp.cmch-pi", FT_UINT8, BASE_DEC, 0, 0x0f,
                "Common Transport Channel Priority Indicator", HFILL
              }
            },
            { &hf_fp_user_buffer_size,
              { "User buffer size",
                "fp.user-buffer-size", FT_UINT16, BASE_DEC, 0, 0x0,
                "User buffer size in octets", HFILL
              }
            },
            { &hf_fp_hsdsch_credits,
              { "HS-DSCH Credits",
                "fp.hsdsch-credits", FT_UINT16, BASE_DEC, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_hsdsch_max_macd_pdu_len,
              { "Max MAC-d PDU Length",
                "fp.hsdsch.max-macd-pdu-len", FT_UINT16, BASE_DEC, 0, 0xfff8,
                "Maximum MAC-d PDU Length in bits", HFILL
              }
            },
            { &hf_fp_hsdsch_max_macdc_pdu_len,
              { "Max MAC-d/c PDU Length",
                "fp.hsdsch.max-macdc-pdu-len", FT_UINT16, BASE_DEC, 0, 0x07ff,
                "Maximum MAC-d/c PDU Length in bits", HFILL
              }
            },
            { &hf_fp_hsdsch_interval,
              { "HS-DSCH Interval in milliseconds",
                "fp.hsdsch-interval", FT_UINT8, BASE_DEC, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_hsdsch_calculated_rate,
              { "Calculated rate allocation (bps)",
                "fp.hsdsch-calculated-rate", FT_UINT32, BASE_DEC, 0, 0x0,
                "Calculated rate RNC is allowed to send in bps", HFILL
              }
            },
            { &hf_fp_hsdsch_unlimited_rate,
              { "Unlimited rate",
                "fp.hsdsch-unlimited-rate", FT_NONE, BASE_NONE, 0, 0x0,
                "No restriction on rate at which date may be sent", HFILL
              }
            },
            { &hf_fp_hsdsch_repetition_period,
              { "HS-DSCH Repetition Period",
                "fp.hsdsch-repetition-period", FT_UINT8, BASE_DEC, 0, 0x0,
                "HS-DSCH Repetition Period in milliseconds", HFILL
              }
            },
            { &hf_fp_hsdsch_data_padding,
              { "Padding",
                "fp.hsdsch-data-padding", FT_UINT8, BASE_DEC, 0, 0xf0,
                "HS-DSCH Repetition Period in milliseconds", HFILL
              }
            },
            { &hf_fp_hsdsch_new_ie_flags,
              { "New IEs flags",
                "fp.hsdsch.new-ie-flags", FT_STRING, BASE_NONE, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_hsdsch_new_ie_flag[0],
              { "DRT IE present",
                "fp.hsdsch.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x80,
                NULL, HFILL
              }
            },
            { &hf_fp_hsdsch_new_ie_flag[1],
              { "New IE present",
                "fp.hsdsch.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x40,
                NULL, HFILL
              }
            },
            { &hf_fp_hsdsch_new_ie_flag[2],
              { "New IE present",
                "fp.hsdsch.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x20,
                NULL, HFILL
              }
            },
            { &hf_fp_hsdsch_new_ie_flag[3],
              { "New IE present",
                "fp.hsdsch.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x10,
                NULL, HFILL
              }
            },
            { &hf_fp_hsdsch_new_ie_flag[4],
              { "New IE present",
                "fp.hsdsch.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x08,
                NULL, HFILL
              }
            },
            { &hf_fp_hsdsch_new_ie_flag[5],
              { "New IE present",
                "fp.hsdsch.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x04,
                NULL, HFILL
              }
            },
            { &hf_fp_hsdsch_new_ie_flag[6],
              { "HS-DSCH physical layer category present",
                "fp.hsdsch.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x02,
                NULL, HFILL
              }
            },
            { &hf_fp_hsdsch_new_ie_flag[7],
              { "Another new IE flags byte",
                "fp.hsdsch.new-ie-flags-byte", FT_UINT8, BASE_DEC, 0, 0x01,
                "Another new IE flagsbyte", HFILL
              }
            },
            { &hf_fp_hsdsch_drt,
              { "DRT",
                "fp.hsdsch.drt", FT_UINT8, BASE_DEC, 0, 0xf0,
                "Delay Reference Time", HFILL
              }
            },
            { &hf_fp_hsdsch_entity,
              { "HS-DSCH Entity",
                "fp.hsdsch.entity", FT_UINT8, BASE_DEC, VALS(hsdshc_mac_entity_vals), 0x0,
                "Type of MAC entity for this HS-DSCH channel", HFILL
              }
            },
            { &hf_fp_timing_advance,
              { "Timing advance",
                "fp.timing-advance", FT_UINT8, BASE_DEC, 0, 0x3f,
                "Timing advance in chips", HFILL
              }
            },
            { &hf_fp_num_of_pdu,
              { "Number of PDUs",
                "fp.hsdsch.num-of-pdu", FT_UINT8, BASE_DEC, 0, 0x0,
                "Number of PDUs in the payload", HFILL
              }
            },
            { &hf_fp_mac_d_pdu_len,
              { "MAC-d PDU Length",
                "fp.hsdsch.mac-d-pdu-len", FT_UINT16, BASE_DEC, 0, 0xfff8,
                "MAC-d PDU Length in bits", HFILL
              }
            },
            { &hf_fp_mac_d_pdu,
              { "MAC-d PDU",
                "fp.mac-d-pdu", FT_BYTES, BASE_NONE, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_data,
              { "Data",
                "fp.data", FT_BYTES, BASE_NONE, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_crcis,
              { "CRCIs",
                "fp.crcis", FT_BYTES, BASE_NONE, NULL, 0x0,
                "CRC Indicators for uplink TBs", HFILL
              }
            },
            { &hf_fp_t1,
              { "T1",
                "fp.t1", FT_FLOAT, BASE_NONE, NULL, 0x0,
                "RNC frame number indicating time it sends frame", HFILL
              }
            },
            { &hf_fp_t2,
              { "T2",
                "fp.t2", FT_FLOAT, BASE_NONE, NULL, 0x0,
                "NodeB frame number indicating time it received DL Sync", HFILL
              }
            },
            { &hf_fp_t3,
              { "T3",
                "fp.t3", FT_FLOAT, BASE_NONE, NULL, 0x0,
                "NodeB frame number indicating time it sends frame", HFILL
              }
            },
            { &hf_fp_ul_sir_target,
              { "UL_SIR_TARGET",
                "fp.ul-sir-target", FT_FLOAT, BASE_NONE, 0, 0x0,
                "Value (in dB) of the SIR target to be used by the UL inner loop power control", HFILL
              }
            },
            { &hf_fp_pusch_set_id,
              { "PUSCH Set Id",
                "fp.pusch-set-id", FT_UINT8, BASE_DEC, NULL, 0x0,
                "Identifies PUSCH Set from those configured in NodeB", HFILL
              }
            },
            { &hf_fp_activation_cfn,
              { "Activation CFN",
                "fp.activation-cfn", FT_UINT8, BASE_DEC, NULL, 0x0,
                "Activation Connection Frame Number", HFILL
              }
            },
            { &hf_fp_duration,
              { "Duration (ms)",
                "fp.pusch-set-id", FT_UINT8, BASE_DEC, NULL, 0x0,
                "Duration of the activation period of the PUSCH Set", HFILL
              }
            },
            { &hf_fp_power_offset,
              { "Power offset",
                "fp.power-offset", FT_FLOAT, BASE_NONE, NULL, 0x0,
                "Power offset (in dB)", HFILL
              }
            },
            { &hf_fp_code_number,
              { "Code number",
                "fp.code-number", FT_UINT8, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_spreading_factor,
              { "Spreading factor",
                "fp.spreading-factor", FT_UINT8, BASE_DEC, VALS(spreading_factor_vals), 0xf0,
                NULL, HFILL
              }
            },
            { &hf_fp_mc_info,
              { "MC info",
                "fp.mc-info", FT_UINT8, BASE_DEC, NULL, 0x0e,
                NULL, HFILL
              }
            },
            { &hf_fp_rach_new_ie_flags,
              { "New IEs flags",
                "fp.rach.new-ie-flags", FT_STRING, BASE_NONE, 0, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_rach_new_ie_flag_unused[0],
              { "New IE present",
                "fp.rach.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x80,
                NULL, HFILL
              }
            },
            { &hf_fp_rach_new_ie_flag_unused[1],
              { "New IE present",
                "fp.rach.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x40,
                NULL, HFILL
              }
            },
            { &hf_fp_rach_new_ie_flag_unused[2],
              { "New IE present",
                "fp.rach.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x20,
                "New IE present (unused)", HFILL
              }
            },
            { &hf_fp_rach_new_ie_flag_unused[3],
              { "New IE present",
                "fp.rach.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x10,
                "New IE present (unused)", HFILL
              }
            },
            { &hf_fp_rach_new_ie_flag_unused[4],
              { "New IE present",
                "fp.rach.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x08,
                "New IE present (unused)", HFILL
              }
            },
            { &hf_fp_rach_new_ie_flag_unused[5],
              { "New IE present",
                "fp.rach.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x04,
                "New IE present (unused)", HFILL
              }
            },
            { &hf_fp_rach_new_ie_flag_unused[6],
              { "New IE present",
                "fp.rach.new-ie-flag", FT_UINT8, BASE_DEC, 0, 0x02,
                "New IE present (unused)", HFILL
              }
            },
            { &hf_fp_rach_cell_portion_id_present,
              { "Cell portion ID present",
                "fp.rach.cell-portion-id-present", FT_UINT8, BASE_DEC, 0, 0x01,
                NULL, HFILL
              }
            },
            { &hf_fp_rach_angle_of_arrival_present,
              { "Angle of arrival present",
                "fp.rach.angle-of-arrival-present", FT_UINT8, BASE_DEC, 0, 0x01,
                NULL, HFILL
              }
            },
            { &hf_fp_rach_ext_propagation_delay_present,
              { "Ext Propagation Delay Present",
                "fp.rach.ext-propagation-delay-present", FT_UINT8, BASE_DEC, 0, 0x02,
                NULL, HFILL
              }
            },
            { &hf_fp_rach_ext_rx_sync_ul_timing_deviation_present,
              { "Ext Received Sync UL Timing Deviation present",
                "fp.rach.ext-rx-sync-ul-timing-deviation-present", FT_UINT8, BASE_DEC, 0, 0x02,
                NULL, HFILL
              }
            },
            { &hf_fp_rach_ext_rx_timing_deviation_present,
              { "Ext Rx Timing Deviation present",
                "fp.rach.ext-rx-timing-deviation-present", FT_UINT8, BASE_DEC, 0, 0x01,
                NULL, HFILL
              }
            },
            { &hf_fp_cell_portion_id,
              { "Cell Portion ID",
                "fp.cell-portion-id", FT_UINT8, BASE_DEC, NULL, 0x3f,
                NULL, HFILL
              }
            },
            { &hf_fp_ext_propagation_delay,
              { "Ext Propagation Delay",
                "fp.ext-propagation-delay", FT_UINT16, BASE_DEC, NULL, 0x03ff,
                NULL, HFILL
              }
            },
            { &hf_fp_angle_of_arrival,
              { "Angle of Arrival",
                "fp.angle-of-arrival", FT_UINT16, BASE_DEC, NULL, 0x03ff,
                NULL, HFILL
              }
            },
            { &hf_fp_ext_received_sync_ul_timing_deviation,
              { "Ext Received SYNC UL Timing Deviation",
                "fp.ext-received-sync-ul-timing-deviation", FT_UINT16, BASE_DEC, NULL, 0x1fff,
                NULL, HFILL
              }
            },


            { &hf_fp_radio_interface_parameter_update_flag[0],
              { "CFN valid",
                "fp.radio-interface-param.cfn-valid", FT_UINT16, BASE_DEC, 0, 0x0001,
                NULL, HFILL
              }
            },
            { &hf_fp_radio_interface_parameter_update_flag[1],
              { "TPC PO valid",
                "fp.radio-interface-param.tpc-po-valid", FT_UINT16, BASE_DEC, 0, 0x0002,
                NULL, HFILL
              }
            },
            { &hf_fp_radio_interface_parameter_update_flag[2],
              { "DPC mode valid",
                "fp.radio-interface-param.dpc-mode-valid", FT_UINT16, BASE_DEC, 0, 0x0004,
                NULL, HFILL
              }
            },
            { &hf_fp_radio_interface_parameter_update_flag[3],
              { "RL sets indicator valid",
                "fp.radio-interface_param.rl-sets-indicator-valid", FT_UINT16, BASE_DEC, 0, 0x0020,
                NULL, HFILL
              }
            },
            { &hf_fp_radio_interface_parameter_update_flag[4],
              { "Maximum UE TX Power valid",
                "fp.radio-interface-param.max-ue-tx-pow-valid", FT_UINT16, BASE_DEC, 0, 0x0040,
                "MAX UE TX POW valid", HFILL
              }
            },
            { &hf_fp_dpc_mode,
              { "DPC Mode",
                "fp.dpc-mode", FT_UINT8, BASE_DEC, NULL, 0x20,
                "DPC Mode to be applied in the uplink", HFILL
              }
            },
            { &hf_fp_tpc_po,
              { "TPC Power Offset",
                "fp.tpc-po", FT_UINT8, BASE_DEC, NULL, 0x1f,
                NULL, HFILL
              }
            },
            { &hf_fp_multiple_rl_set_indicator,
              { "Multiple RL sets indicator",
                "fp.multiple-rl-sets-indicator", FT_UINT8, BASE_DEC, NULL, 0x80,
                NULL, HFILL
              }
            },
            { &hf_fp_max_ue_tx_pow,
              { "Maximum UE TX Power",
                "fp.max-ue-tx-pow", FT_INT8, BASE_DEC, NULL, 0x0,
                "Max UE TX POW (dBm)", HFILL
              }
            },
            { &hf_fp_congestion_status,
              { "Congestion Status",
                "fp.congestion-status", FT_UINT8, BASE_DEC, VALS(congestion_status_vals), 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_e_rucch_present,
              { "E-RUCCH Present",
                "fp.erucch-present", FT_UINT8, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_extended_bits_present,
              { "Extended Bits Present",
                "fp.extended-bits-present", FT_UINT8, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_extended_bits,
              { "Extended Bits",
                "fp.extended-bits", FT_UINT8, BASE_HEX, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_spare_extension,
              { "Spare Extension",
                "fp.spare-extension", FT_NONE, BASE_NONE, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_ul_setup_frame,
              { "UL setup frame",
                "fp.ul.setup_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_dl_setup_frame,
              { "DL setup frame",
                "fp.dl.setup_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
                NULL, HFILL
              }
            },
            { &hf_fp_relevant_pi_frame,
              { "Paging Indications frame number",
                "fp.pch.relevant-pi-frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
                "The frame where this Paging Indication bitmap was found",
                HFILL
              }
            },
            { &hf_fp_hsdsch_physical_layer_category,
              { "HS-DSCH physical layer category",
                "fp.hsdsch.physical_layer_category", FT_UINT8, BASE_DEC, NULL, 0x0,
                NULL, HFILL
              }
            }
        };


    static gint *ett[] =
    {
        &ett_fp,
        &ett_fp_data,
        &ett_fp_crcis,
        &ett_fp_ddi_config,
        &ett_fp_edch_subframe_header,
        &ett_fp_edch_subframe,
        &ett_fp_edch_maces,
        &ett_fp_edch_macis_descriptors,
        &ett_fp_hsdsch_new_ie_flags,
        &ett_fp_rach_new_ie_flags,
        &ett_fp_hsdsch_pdu_block_header,
        &ett_fp_pch_relevant_pi,
        &ett_fp_release
    };

    static ei_register_info ei[] = {
        { &ei_fp_bad_header_checksum, { "fp.header.bad_checksum", PI_CHECKSUM, PI_WARN, "Bad header checksum.", EXPFILL }},
        { &ei_fp_crci_no_subdissector, { "fp.crci.no_subdissector", PI_UNDECODED, PI_NOTE, "Not sent to subdissectors as CRCI is set", EXPFILL }},
        { &ei_fp_crci_error_bit_set_for_tb, { "fp.crci.error_bit_set_for_tb", PI_CHECKSUM, PI_WARN, "CRCI error bit set for TB", EXPFILL }},
        { &ei_fp_spare_extension, { "fp.spare-extension.expert", PI_UNDECODED, PI_WARN, "Spare Extension present (%u bytes)", EXPFILL }},
        { &ei_fp_bad_payload_checksum, { "fp.payload-crc.bad", PI_CHECKSUM, PI_WARN, "Bad payload checksum.", EXPFILL }},
        { &ei_fp_stop_hsdpa_transmission, { "fp.stop_hsdpa_transmission", PI_RESPONSE_CODE, PI_NOTE, "Stop HSDPA transmission", EXPFILL }},
        { &ei_fp_timing_adjustmentment_reported, { "fp.timing_adjustmentment_reported", PI_SEQUENCE, PI_WARN, "Timing adjustmentment reported (%.3f ms)", EXPFILL }},
        { &ei_fp_expecting_tdd, { "fp.expecting_tdd", PI_MALFORMED, PI_NOTE, "Error: expecting TDD-384 or TDD-768", EXPFILL }},
        { &ei_fp_ddi_not_defined, { "fp.ddi_not_defined", PI_MALFORMED, PI_ERROR, "DDI %u not defined for this UE!", EXPFILL }},
        { &ei_fp_unable_to_locate_ddi_entry, { "fp.unable_to_locate_ddi_entry", PI_UNDECODED, PI_ERROR, "Unable to locate DDI entry.", EXPFILL }},
        { &ei_fp_mac_is_sdus_miscount, { "fp.mac_is_sdus.miscount", PI_MALFORMED, PI_ERROR, "Found too many (%u) MAC-is SDUs - header said there were %u", EXPFILL }},
        { &ei_fp_e_rnti_t2_edch_frames, { "fp.e_rnti.t2_edch_frames", PI_MALFORMED, PI_ERROR, "E-RNTI not supposed to appear for T2 EDCH frames", EXPFILL }},
        { &ei_fp_e_rnti_first_entry, { "fp.e_rnti.first_entry", PI_MALFORMED, PI_ERROR, "E-RNTI must be first entry among descriptors", EXPFILL }},
        { &ei_fp_maybe_srb, { "fp.maybe_srb", PI_PROTOCOL, PI_NOTE, "Found MACd-Flow = 0 and not MUX detected. (This might be SRB)", EXPFILL }},
        { &ei_fp_transport_channel_type_unknown, { "fp.transport_channel_type.unknown", PI_UNDECODED, PI_WARN, "Unknown transport channel type", EXPFILL }},
        { &ei_fp_pch_lost_relevant_pi_frame, { "fp.pch_lost_relevant_pi_frame", PI_SEQUENCE, PI_WARN, "Previous PCH frame containing PI bitmap not captured (common at capture start)", EXPFILL }},
        { &ei_fp_hsdsch_entity_not_specified, { "fp.hsdsch_entity_not_specified", PI_MALFORMED, PI_ERROR, "HSDSCH Entity not specified", EXPFILL }},
        { &ei_fp_hsdsch_common_experimental_support, { "fp.hsdsch_common.experimental_support", PI_DEBUG, PI_WARN, "HSDSCH COMMON - Experimental support!", EXPFILL }},
        { &ei_fp_hsdsch_common_t3_not_implemented, { "fp.hsdsch_common_t3.not_implemented", PI_DEBUG, PI_ERROR, "HSDSCH COMMON T3 - Not implemeneted!", EXPFILL }},
        { &ei_fp_channel_type_unknown, { "fp.channel_type.unknown", PI_MALFORMED, PI_ERROR, "Unknown channel type", EXPFILL }},
        { &ei_fp_no_per_frame_info, { "fp.no_per_frame_info", PI_UNDECODED, PI_ERROR, "Can't dissect FP frame because no per-frame info was attached!", EXPFILL }},
        { &ei_fp_no_per_conv_channel_info, { "fp.no_per_conv_channel_info", PI_UNDECODED, PI_ERROR, "Can't dissect this FP stream because no per-conversation channel info was attached!", EXPFILL }},
        { &ei_fp_invalid_frame_count, { "fp.invalid_frame_count", PI_MALFORMED, PI_ERROR, "Invalid frame count", EXPFILL }},
    };

    module_t *fp_module;
    expert_module_t *expert_fp;

    /* Register protocol. */
    proto_fp = proto_register_protocol("FP", "FP", "fp");
    proto_register_field_array(proto_fp, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
    expert_fp = expert_register_protocol(proto_fp);
    expert_register_field_array(expert_fp, ei, array_length(ei));

    /* Allow other dissectors to find this one by name. */
    fp_handle = register_dissector("fp", dissect_fp, proto_fp);

    /* Preferences */
    fp_module = prefs_register_protocol(proto_fp, NULL);

    /* Determines whether release information should be displayed */
    prefs_register_bool_preference(fp_module, "show_release_info",
                                   "Show reported release info",
                                   "Show reported release info",
                                   &preferences_show_release_info);

    /* Determines whether MAC dissector should be called for payloads */
    prefs_register_bool_preference(fp_module, "call_mac",
                                   "Call MAC dissector for payloads",
                                   "Call MAC dissector for payloads",
                                   &preferences_call_mac_dissectors);
     /* Determines whether or not to validate FP payload checksums */
    prefs_register_bool_preference(fp_module, "payload_checksum",
                                    "Validate FP payload checksums",
                                    "Validate FP payload checksums",
                                    &preferences_payload_checksum);
     /* Determines whether or not to validate FP header checksums */
    prefs_register_bool_preference(fp_module, "header_checksum",
                                    "Validate FP header checksums",
                                    "Validate FP header checksums",
                                    &preferences_header_checksum);
     /* Determines whether or not to track Paging Indications between PCH frames*/
     prefs_register_bool_preference(fp_module, "track_paging_indications",
                                    "Track Paging Indications in PCH channels",
                                    "For each PCH data frame, Try to show the paging indications bitmap found in the previous frame",
                                    &preferences_track_paging_indications);
    prefs_register_obsolete_preference(fp_module, "udp_heur");
    prefs_register_obsolete_preference(fp_module, "epandchannelconfigurationtable");

}


void proto_reg_handoff_fp(void)
{
    dissector_handle_t fp_aal2_handle;

    rlc_bcch_handle           = find_dissector_add_dependency("rlc.bcch", proto_fp);
    mac_fdd_rach_handle       = find_dissector_add_dependency("mac.fdd.rach", proto_fp);
    mac_fdd_fach_handle       = find_dissector_add_dependency("mac.fdd.fach", proto_fp);
    mac_fdd_pch_handle        = find_dissector_add_dependency("mac.fdd.pch", proto_fp);
    mac_fdd_dch_handle        = find_dissector_add_dependency("mac.fdd.dch", proto_fp);
    mac_fdd_edch_handle       = find_dissector_add_dependency("mac.fdd.edch", proto_fp);
    mac_fdd_edch_type2_handle = find_dissector_add_dependency("mac.fdd.edch.type2", proto_fp);
    mac_fdd_hsdsch_handle     = find_dissector_add_dependency("mac.fdd.hsdsch", proto_fp);

    heur_dissector_add("udp", heur_dissect_fp, "FP over UDP", "fp_udp", proto_fp, HEURISTIC_DISABLE);
    heur_dissector_add("fp_mux", heur_dissect_fp, "FP over FP Mux", "fp_fp_mux", proto_fp, HEURISTIC_ENABLE);

    fp_aal2_handle = create_dissector_handle(dissect_fp_aal2, proto_fp);
    dissector_add_uint("atm.aal2.type", TRAF_UMTS_FP, fp_aal2_handle);
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */
